mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-18 16:53:57 +00:00
Compare commits
116 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b8a0af8e9 | ||
|
|
31319b0811 | ||
|
|
9976812bf7 | ||
|
|
6b4d422b65 | ||
|
|
ec9db3a48f | ||
|
|
72cde368f5 | ||
|
|
bd77aee3c2 | ||
|
|
dafdda1bc7 | ||
|
|
6c6a238c5f | ||
|
|
0df18022ed | ||
|
|
6626368613 | ||
|
|
cff728bbbe | ||
|
|
3961b53d0e | ||
|
|
3f08e87eda | ||
|
|
b84c34a69c | ||
|
|
c2e1a092c9 | ||
|
|
fe03894054 | ||
|
|
893695eda0 | ||
|
|
28727600fe | ||
|
|
d892ed4f34 | ||
|
|
ea618febe2 | ||
|
|
358eac061f | ||
|
|
33f9e5729b | ||
|
|
2a7feba5dc | ||
|
|
1390f99895 | ||
|
|
daf8ee6ba2 | ||
|
|
652fe69141 | ||
|
|
61b2710219 | ||
|
|
0e67e21223 | ||
|
|
f12784b6e7 | ||
|
|
8795359c0b | ||
|
|
bcc2127a6e | ||
|
|
766c5760d7 | ||
|
|
3c8db34456 | ||
|
|
433ebf55ac | ||
|
|
b68bed97dd | ||
|
|
2a8600a841 | ||
|
|
3fd48eb784 | ||
|
|
aa19241b2b | ||
|
|
a16a75ff39 | ||
|
|
e4d97e9775 | ||
|
|
5688aaf0e8 | ||
|
|
ad5eb22553 | ||
|
|
1c3a5f4800 | ||
|
|
d8a7275512 | ||
|
|
489dd710fa | ||
|
|
0db93c7dad | ||
|
|
d9fe3c709e | ||
|
|
03a3d734f1 | ||
|
|
106d66f1a2 | ||
|
|
3ef6472825 | ||
|
|
df77d754ae | ||
|
|
6a8da74bcf | ||
|
|
1aa3bb1ad2 | ||
|
|
fdcf244f4e | ||
|
|
d8a4eaaa66 | ||
|
|
1c71c20b48 | ||
|
|
aefc5eca49 | ||
|
|
5f1b83a0d9 | ||
|
|
85efc23c25 | ||
|
|
b344a1850a | ||
|
|
497f5e7742 | ||
|
|
199b0d5b19 | ||
|
|
2554dc4c69 | ||
|
|
c4c401e054 | ||
|
|
864ff0070f | ||
|
|
da8c40d5dc | ||
|
|
a32fdf3385 | ||
|
|
c9c1750afb | ||
|
|
857063d2c7 | ||
|
|
ed699a8dee | ||
|
|
a9ec1a62bf | ||
|
|
778e921594 | ||
|
|
206886c091 | ||
|
|
af2896efc3 | ||
|
|
c7735362e0 | ||
|
|
886e176b08 | ||
|
|
f522a7c9ef | ||
|
|
69f4ce821f | ||
|
|
8bfd6e5547 | ||
|
|
1cbde684e7 | ||
|
|
e5f7e9addc | ||
|
|
6832c11d06 | ||
|
|
740cc2ec7c | ||
|
|
e12c6daa16 | ||
|
|
2ee8a64945 | ||
|
|
a54edd8b4a | ||
|
|
f7b6a91621 | ||
|
|
96dbfe8593 | ||
|
|
8513f83169 | ||
|
|
b116e29dc0 | ||
|
|
52b291fb24 | ||
|
|
dddc5bfd96 | ||
|
|
282bb45869 | ||
|
|
c982ebe33a | ||
|
|
08935ceabd | ||
|
|
0e7a0d1afa | ||
|
|
38dc8bdea5 | ||
|
|
c33aa3c874 | ||
|
|
afa34c74d7 | ||
|
|
cda9b8fb42 | ||
|
|
026abd7909 | ||
|
|
bc31a28eee | ||
|
|
f1624441b4 | ||
|
|
358abde963 | ||
|
|
c65fc15d99 | ||
|
|
b32226273e | ||
|
|
2e7a2a05a5 | ||
|
|
ab70ff10be | ||
|
|
6a429f9f39 | ||
|
|
b03c69b185 | ||
|
|
3fe77f4961 | ||
|
|
79be151de0 | ||
|
|
e804b92f30 | ||
|
|
cf31b36961 | ||
|
|
3156baf78e |
@@ -1,2 +1,4 @@
|
|||||||
MAIN_VITE_API_URL=API_URL
|
MAIN_VITE_API_URL=API_URL
|
||||||
MAIN_VITE_AUTH_URL=AUTH_URL
|
MAIN_VITE_AUTH_URL=AUTH_URL
|
||||||
|
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID=
|
||||||
|
RENDERER_VITE_TORBOX_REFERRAL_CODE=
|
||||||
|
|||||||
8
.github/workflows/build.yml
vendored
8
.github/workflows/build.yml
vendored
@@ -47,12 +47,13 @@ jobs:
|
|||||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
|
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
|
||||||
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }}
|
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }}
|
||||||
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }}
|
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }}
|
||||||
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
|
|
||||||
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
||||||
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
||||||
|
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: ${{ vars.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID }}
|
||||||
|
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
|
||||||
|
|
||||||
- name: Build Windows
|
- name: Build Windows
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
@@ -61,14 +62,15 @@ jobs:
|
|||||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
|
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_STAGING_API_URL }}
|
||||||
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }}
|
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_STAGING_AUTH_URL }}
|
||||||
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }}
|
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_STAGING_CHECKOUT_URL }}
|
||||||
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
|
|
||||||
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
||||||
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
||||||
|
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: ${{ vars.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID }}
|
||||||
|
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
|
||||||
|
|
||||||
- name: Test Upload build
|
- name: Upload build
|
||||||
env:
|
env:
|
||||||
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
BRANCH_NAME: ${{ github.head_ref || github.ref_name }}
|
||||||
S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
|
S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
|
||||||
|
|||||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -49,13 +49,13 @@ jobs:
|
|||||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
|
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
|
||||||
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
|
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
|
||||||
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_URL }}
|
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_URL }}
|
||||||
MAIN_VITE_ANALYTICS_API_URL: ${{ vars.MAIN_VITE_ANALYTICS_API_URL }}
|
|
||||||
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
|
|
||||||
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
||||||
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
||||||
|
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: ${{ vars.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID }}
|
||||||
|
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
|
||||||
|
|
||||||
- name: Build Windows
|
- name: Build Windows
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
@@ -64,13 +64,13 @@ jobs:
|
|||||||
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
|
MAIN_VITE_API_URL: ${{ vars.MAIN_VITE_API_URL }}
|
||||||
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
|
MAIN_VITE_AUTH_URL: ${{ vars.MAIN_VITE_AUTH_URL }}
|
||||||
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_URL }}
|
MAIN_VITE_CHECKOUT_URL: ${{ vars.MAIN_VITE_CHECKOUT_URL }}
|
||||||
MAIN_VITE_ANALYTICS_API_URL: ${{ vars.MAIN_VITE_ANALYTICS_API_URL }}
|
|
||||||
RENDERER_VITE_INTERCOM_APP_ID: ${{ vars.RENDERER_VITE_INTERCOM_APP_ID }}
|
|
||||||
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
RENDERER_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
||||||
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
MAIN_VITE_EXTERNAL_RESOURCES_URL: ${{ vars.EXTERNAL_RESOURCES_URL }}
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
RENDERER_VITE_SENTRY_DSN: ${{ vars.SENTRY_DSN }}
|
||||||
|
RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: ${{ vars.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID }}
|
||||||
|
RENDERER_VITE_TORBOX_REFERRAL_CODE: ${{ vars.RENDERER_VITE_TORBOX_REFERRAL_CODE }}
|
||||||
|
|
||||||
- name: Create artifact
|
- name: Create artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
|
|||||||
13
.github/workflows/trigger-lp.yml
vendored
Normal file
13
.github/workflows/trigger-lp.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
name: Trigger Landing Page Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Trigger Landing Page build
|
||||||
|
run: curl --location --request POST '${{ secrets.LP_TRIGGER_DEPLOY_URL }}'
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydralauncher",
|
"name": "hydralauncher",
|
||||||
"version": "3.2.2",
|
"version": "3.3.1",
|
||||||
"description": "Hydra",
|
"description": "Hydra",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
"author": "Los Broxas",
|
"author": "Los Broxas",
|
||||||
@@ -44,6 +44,7 @@
|
|||||||
"@sentry/vite-plugin": "^2.22.7",
|
"@sentry/vite-plugin": "^2.22.7",
|
||||||
"auto-launch": "^5.0.6",
|
"auto-launch": "^5.0.6",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
|
"axios-cookiejar-support": "^5.0.5",
|
||||||
"better-sqlite3": "^11.7.0",
|
"better-sqlite3": "^11.7.0",
|
||||||
"classic-level": "^2.0.0",
|
"classic-level": "^2.0.0",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
@@ -71,9 +72,11 @@
|
|||||||
"react-loading-skeleton": "^3.4.0",
|
"react-loading-skeleton": "^3.4.0",
|
||||||
"react-redux": "^9.1.1",
|
"react-redux": "^9.1.1",
|
||||||
"react-router-dom": "^6.22.3",
|
"react-router-dom": "^6.22.3",
|
||||||
|
"react-tooltip": "^5.28.0",
|
||||||
"sound-play": "^1.1.0",
|
"sound-play": "^1.1.0",
|
||||||
"sudo-prompt": "^9.2.1",
|
"sudo-prompt": "^9.2.1",
|
||||||
"tar": "^7.4.3",
|
"tar": "^7.4.3",
|
||||||
|
"tough-cookie": "^5.1.1",
|
||||||
"user-agents": "^1.1.387",
|
"user-agents": "^1.1.387",
|
||||||
"yaml": "^2.6.1",
|
"yaml": "^2.6.1",
|
||||||
"yup": "^1.5.0",
|
"yup": "^1.5.0",
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -255,6 +255,12 @@
|
|||||||
"download_sources_description": "سيقوم Hydra بجلب روابط التنزيل من هذه المصادر. يجب أن يكون عنوان URL للمصدر رابطًا مباشرًا لملف .json يحتوي على روابط التنزيل.",
|
"download_sources_description": "سيقوم Hydra بجلب روابط التنزيل من هذه المصادر. يجب أن يكون عنوان URL للمصدر رابطًا مباشرًا لملف .json يحتوي على روابط التنزيل.",
|
||||||
"validate_download_source": "تحقق",
|
"validate_download_source": "تحقق",
|
||||||
"remove_download_source": "إزالة",
|
"remove_download_source": "إزالة",
|
||||||
|
"removed_download_sources": "تمت إزالة مصادر التنزيل",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "لا",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "نعم، احذف كل شيء",
|
||||||
|
"description_confirmation_delete_all_sources": "سوف تقوم بحذف جميع مصادر التنزيل",
|
||||||
|
"title_confirmation_delete_all_sources": "احذف جميع مصادر التنزيل",
|
||||||
|
"button_delete_all_sources": "قم بإزالة جميع مصادر التنزيل",
|
||||||
"add_download_source": "إضافة مصدر",
|
"add_download_source": "إضافة مصدر",
|
||||||
"download_count_zero": "لا توجد خيارات تنزيل",
|
"download_count_zero": "لا توجد خيارات تنزيل",
|
||||||
"download_count_one": "{{countFormatted}} خيار تنزيل",
|
"download_count_one": "{{countFormatted}} خيار تنزيل",
|
||||||
|
|||||||
@@ -107,6 +107,12 @@
|
|||||||
"notifications": "Апавяшчэнні",
|
"notifications": "Апавяшчэнні",
|
||||||
"enable_download_notifications": "Па сканчэнні сцягванні",
|
"enable_download_notifications": "Па сканчэнні сцягванні",
|
||||||
"enable_repack_list_notifications": "Пры даданні новага рэпака",
|
"enable_repack_list_notifications": "Пры даданні новага рэпака",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Няма",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Так,выдаліць усё",
|
||||||
|
"description_confirmation_delete_all_sources": "Вы выдаліце ўсе крыніцы сцягвання",
|
||||||
|
"title_confirmation_delete_all_sources": "Выдаліць усе крыніцы сцягвання",
|
||||||
|
"removed_download_sources": "Крыніцы сцягвання выдалены",
|
||||||
|
"button_delete_all_sources": "Премахнете всички източници на изтегляне",
|
||||||
"behavior": "Паводзіны",
|
"behavior": "Паводзіны",
|
||||||
"quit_app_instead_hiding": "Закрываць праграму замест таго, каб хаваць яе ў трэй",
|
"quit_app_instead_hiding": "Закрываць праграму замест таго, каб хаваць яе ў трэй",
|
||||||
"launch_with_system": "Запускаць праграму пры запуску сыстэмы"
|
"launch_with_system": "Запускаць праграму пры запуску сыстэмы"
|
||||||
|
|||||||
@@ -253,6 +253,12 @@
|
|||||||
"download_source_errored": "Сгрешен",
|
"download_source_errored": "Сгрешен",
|
||||||
"sync_download_sources": "Синхронизирай източниците",
|
"sync_download_sources": "Синхронизирай източниците",
|
||||||
"removed_download_source": "Източника за сваляне е премахнат",
|
"removed_download_source": "Източника за сваляне е премахнат",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "не",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Да, удалить все",
|
||||||
|
"description_confirmation_delete_all_sources": "Вы удалите все источники загрузки",
|
||||||
|
"title_confirmation_delete_all_sources": "Удалить все источники загрузки",
|
||||||
|
"removed_download_sources": "Шрифты удалены",
|
||||||
|
"button_delete_all_sources": "Удалить все источники загрузки",
|
||||||
"added_download_source": "Добавен източник за сваляне",
|
"added_download_source": "Добавен източник за сваляне",
|
||||||
"download_sources_synced": "Всички източници за сваляне са синхронизирани",
|
"download_sources_synced": "Всички източници за сваляне са синхронизирани",
|
||||||
"insert_valid_json_url": "Добавете ваиден JSON линк",
|
"insert_valid_json_url": "Добавете ваиден JSON линк",
|
||||||
|
|||||||
@@ -175,6 +175,12 @@
|
|||||||
"download_sources_description": "Hydra buscarà els enllaços de descàrrega d'aquestes fonts. L'URL d'origen ha de ser un enllaç directe a un fitxer .json que contingui els enllaços de descàrrega.",
|
"download_sources_description": "Hydra buscarà els enllaços de descàrrega d'aquestes fonts. L'URL d'origen ha de ser un enllaç directe a un fitxer .json que contingui els enllaços de descàrrega.",
|
||||||
"validate_download_source": "Valida",
|
"validate_download_source": "Valida",
|
||||||
"remove_download_source": "Elimina",
|
"remove_download_source": "Elimina",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "No",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Sí, esborra-ho tot",
|
||||||
|
"description_confirmation_delete_all_sources": "Eliminareu totes les fonts de descàrrega",
|
||||||
|
"title_confirmation_delete_all_sources": "Suprimeix totes les fonts de baixada",
|
||||||
|
"removed_download_sources": "S'han eliminat les fonts de descàrrega",
|
||||||
|
"button_delete_all_sources": "Elimina totes les fonts de baixada",
|
||||||
"add_download_source": "Afegeix font",
|
"add_download_source": "Afegeix font",
|
||||||
"download_count_zero": "No hi ha baixades a la llista",
|
"download_count_zero": "No hi ha baixades a la llista",
|
||||||
"download_count_one": "{{countFormatted}} a la llista de baixades",
|
"download_count_one": "{{countFormatted}} a la llista de baixades",
|
||||||
|
|||||||
@@ -237,6 +237,12 @@
|
|||||||
"download_source_errored": "Chyba",
|
"download_source_errored": "Chyba",
|
||||||
"sync_download_sources": "Synchronizovat zdroje",
|
"sync_download_sources": "Synchronizovat zdroje",
|
||||||
"removed_download_source": "Zdroj odebrán",
|
"removed_download_source": "Zdroj odebrán",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Žádný",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Ano, smazat vše",
|
||||||
|
"description_confirmation_delete_all_sources": "Smažete všechny zdroje stahování",
|
||||||
|
"title_confirmation_delete_all_sources": "Odstraňte všechny zdroje stahování",
|
||||||
|
"removed_download_sources": "Zdroje stahování byly odstraněny",
|
||||||
|
"button_delete_all_sources": "Odstraňte všechny zdroje stahování",
|
||||||
"added_download_source": "Zdroj přidán",
|
"added_download_source": "Zdroj přidán",
|
||||||
"download_sources_synced": "Všechny zdroje jsou synchronizovány",
|
"download_sources_synced": "Všechny zdroje jsou synchronizovány",
|
||||||
"insert_valid_json_url": "Zadej platnou JSON adresu",
|
"insert_valid_json_url": "Zadej platnou JSON adresu",
|
||||||
|
|||||||
@@ -201,6 +201,12 @@
|
|||||||
"download_source_errored": "Fejlede",
|
"download_source_errored": "Fejlede",
|
||||||
"sync_download_sources": "Synkronisér kilder",
|
"sync_download_sources": "Synkronisér kilder",
|
||||||
"removed_download_source": "Download kilde fjernet",
|
"removed_download_source": "Download kilde fjernet",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Ingen",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Ja, slet alt",
|
||||||
|
"description_confirmation_delete_all_sources": "Du vil slette alle downloadkilder",
|
||||||
|
"title_confirmation_delete_all_sources": "Slet alle downloadkilder",
|
||||||
|
"removed_download_sources": "Download kilder fjernet",
|
||||||
|
"button_delete_all_sources": "Fjern alle downloadkilder",
|
||||||
"added_download_source": "Tilføjede download kilde",
|
"added_download_source": "Tilføjede download kilde",
|
||||||
"download_sources_synced": "Alle download kilder er synkroniserede",
|
"download_sources_synced": "Alle download kilder er synkroniserede",
|
||||||
"insert_valid_json_url": "Indsæt en gyldig JSON url",
|
"insert_valid_json_url": "Indsæt en gyldig JSON url",
|
||||||
|
|||||||
@@ -185,6 +185,12 @@
|
|||||||
"download_source_errored": "Fehlgeschlagen",
|
"download_source_errored": "Fehlgeschlagen",
|
||||||
"sync_download_sources": "Quellen synchronisieren",
|
"sync_download_sources": "Quellen synchronisieren",
|
||||||
"removed_download_source": "Download-Quelle entfernt",
|
"removed_download_source": "Download-Quelle entfernt",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Nein",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Ja, alles löschen",
|
||||||
|
"description_confirmation_delete_all_sources": "Du löschen alle Downloadquellen",
|
||||||
|
"title_confirmation_delete_all_sources": "Löschen du alle Downloadquellen",
|
||||||
|
"removed_download_sources": "Download-Quellen entfernt",
|
||||||
|
"button_delete_all_sources": "Entfernen Sie alle Downloadquellen",
|
||||||
"added_download_source": "Download-Quelle hinzugefügt",
|
"added_download_source": "Download-Quelle hinzugefügt",
|
||||||
"download_sources_synced": "Alle Download-Quellen sind synchronisiert",
|
"download_sources_synced": "Alle Download-Quellen sind synchronisiert",
|
||||||
"insert_valid_json_url": "Füge eine gültige JSON URL ein",
|
"insert_valid_json_url": "Füge eine gültige JSON URL ein",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"manage_files_description": "Manage which files will be backed up and restored",
|
"manage_files_description": "Manage which files will be backed up and restored",
|
||||||
"select_folder": "Select folder",
|
"select_folder": "Select folder",
|
||||||
"backup_from": "Backup from {{date}}",
|
"backup_from": "Backup from {{date}}",
|
||||||
|
"automatic_backup_from": "Automatic backup from {{date}}",
|
||||||
|
"enable_automatic_cloud_sync": "Enable automatic cloud sync",
|
||||||
"custom_backup_location_set": "Custom backup location set",
|
"custom_backup_location_set": "Custom backup location set",
|
||||||
"no_directory_selected": "No directory selected",
|
"no_directory_selected": "No directory selected",
|
||||||
"no_write_permission": "Cannot download into this directory. Click here to learn more.",
|
"no_write_permission": "Cannot download into this directory. Click here to learn more.",
|
||||||
@@ -265,6 +267,12 @@
|
|||||||
"download_source_errored": "Errored",
|
"download_source_errored": "Errored",
|
||||||
"sync_download_sources": "Sync sources",
|
"sync_download_sources": "Sync sources",
|
||||||
"removed_download_source": "Download source removed",
|
"removed_download_source": "Download source removed",
|
||||||
|
"removed_download_sources": "Download sources removed",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "No",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Yes, delete everything",
|
||||||
|
"title_confirmation_delete_all_sources": "Delete all download sources",
|
||||||
|
"description_confirmation_delete_all_sources": "You will delete all download sources",
|
||||||
|
"button_delete_all_sources": "Remove all",
|
||||||
"added_download_source": "Added download source",
|
"added_download_source": "Added download source",
|
||||||
"download_sources_synced": "All download sources are synced",
|
"download_sources_synced": "All download sources are synced",
|
||||||
"insert_valid_json_url": "Insert a valid JSON url",
|
"insert_valid_json_url": "Insert a valid JSON url",
|
||||||
@@ -327,12 +335,16 @@
|
|||||||
"enable_torbox": "Enable Torbox",
|
"enable_torbox": "Enable Torbox",
|
||||||
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
|
"torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.",
|
||||||
"torbox_account_linked": "TorBox account linked",
|
"torbox_account_linked": "TorBox account linked",
|
||||||
|
"create_real_debrid_account": "Click here if you don't have a Real-Debrid account yet",
|
||||||
|
"create_torbox_account": "Click here if you don't have a TorBox account yet",
|
||||||
"real_debrid_account_linked": "Real-Debrid account linked",
|
"real_debrid_account_linked": "Real-Debrid account linked",
|
||||||
"name_min_length": "Theme name must be at least 3 characters long",
|
"name_min_length": "Theme name must be at least 3 characters long",
|
||||||
"import_theme": "Import theme",
|
"import_theme": "Import theme",
|
||||||
"import_theme_description": "You will import {{theme}} from the theme store",
|
"import_theme_description": "You will import {{theme}} from the theme store",
|
||||||
"error_importing_theme": "Error importing theme",
|
"error_importing_theme": "Error importing theme",
|
||||||
"theme_imported": "Theme imported successfully"
|
"theme_imported": "Theme imported successfully",
|
||||||
|
"enable_friend_request_notifications": "When a friend request is received",
|
||||||
|
"enable_auto_install": "Download updates automatically"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Download complete",
|
"download_complete": "Download complete",
|
||||||
@@ -343,13 +355,17 @@
|
|||||||
"new_update_available": "Version {{version}} available",
|
"new_update_available": "Version {{version}} available",
|
||||||
"restart_to_install_update": "Restart Hydra to install the update",
|
"restart_to_install_update": "Restart Hydra to install the update",
|
||||||
"notification_achievement_unlocked_title": "Achievement unlocked for {{game}}",
|
"notification_achievement_unlocked_title": "Achievement unlocked for {{game}}",
|
||||||
"notification_achievement_unlocked_body": "{{achievement}} and other {{count}} were unlocked"
|
"notification_achievement_unlocked_body": "{{achievement}} and other {{count}} were unlocked",
|
||||||
|
"new_friend_request_description": "You have received a new friend request",
|
||||||
|
"new_friend_request_title": "New friend request"
|
||||||
},
|
},
|
||||||
"system_tray": {
|
"system_tray": {
|
||||||
"open": "Open Hydra",
|
"open": "Open Hydra",
|
||||||
"quit": "Quit"
|
"quit": "Quit"
|
||||||
},
|
},
|
||||||
"game_card": {
|
"game_card": {
|
||||||
|
"available_one": "Available",
|
||||||
|
"available_other": "Available",
|
||||||
"no_downloads": "No downloads available"
|
"no_downloads": "No downloads available"
|
||||||
},
|
},
|
||||||
"binary_not_found_modal": {
|
"binary_not_found_modal": {
|
||||||
@@ -444,9 +460,6 @@
|
|||||||
"show_achievements_on_profile": "Show your achievements on your profile",
|
"show_achievements_on_profile": "Show your achievements on your profile",
|
||||||
"show_points_on_profile": "Show your earned points on your profile"
|
"show_points_on_profile": "Show your earned points on your profile"
|
||||||
},
|
},
|
||||||
"badge": {
|
|
||||||
"badge_description_theme_creator": "Awarded to those who created a custom theme"
|
|
||||||
},
|
|
||||||
"achievement": {
|
"achievement": {
|
||||||
"achievement_unlocked": "Achievement unlocked",
|
"achievement_unlocked": "Achievement unlocked",
|
||||||
"user_achievements": "{{displayName}}'s Achievements",
|
"user_achievements": "{{displayName}}'s Achievements",
|
||||||
|
|||||||
@@ -174,6 +174,8 @@
|
|||||||
"manage_files_description": "Gestiona los archivos que serán respaldados y restaurados",
|
"manage_files_description": "Gestiona los archivos que serán respaldados y restaurados",
|
||||||
"select_folder": "Seleccionar carpeta",
|
"select_folder": "Seleccionar carpeta",
|
||||||
"backup_from": "Copia de seguridad de {{date}}",
|
"backup_from": "Copia de seguridad de {{date}}",
|
||||||
|
"automatic_backup_from": "Copia de seguridad automática de {{date}}",
|
||||||
|
"enable_automatic_cloud_sync": "Habilitar sincronización automática en la nube",
|
||||||
"custom_backup_location_set": "Se configuró la carpeta de copia de seguridad",
|
"custom_backup_location_set": "Se configuró la carpeta de copia de seguridad",
|
||||||
"clear": "Limpiar",
|
"clear": "Limpiar",
|
||||||
"no_directory_selected": "No se seleccionó un directorio",
|
"no_directory_selected": "No se seleccionó un directorio",
|
||||||
@@ -185,7 +187,13 @@
|
|||||||
"reset_achievements_description": "Esto reiniciará todos los logros de {{game}}",
|
"reset_achievements_description": "Esto reiniciará todos los logros de {{game}}",
|
||||||
"reset_achievements_title": "¿Estás seguro?",
|
"reset_achievements_title": "¿Estás seguro?",
|
||||||
"reset_achievements_success": "Logros reiniciados exitosamente",
|
"reset_achievements_success": "Logros reiniciados exitosamente",
|
||||||
"reset_achievements_error": "Se produjo un error al reiniciar los logros"
|
"reset_achievements_error": "Se produjo un error al reiniciar los logros",
|
||||||
|
"download_error_gofile_quota_exceeded": "Has excedido la cuota mensual de Gofile. Por favor espera a que se reinicie la cuota.",
|
||||||
|
"download_error_real_debrid_account_not_authorized": "Tu cuenta de Real-Debrid no está autorizada para nueva descargas. Por favor, revisa los ajustes de tu cuenta e intenta de nuevo.",
|
||||||
|
"download_error_not_cached_in_real_debrid": "Esta descarga no está disponible en Real-Debrid y el estado de descarga del sondeo de Real-Debrid aún no está disponible.",
|
||||||
|
"download_error_not_cached_in_torbox": "Esta descarga no está disponible en Torbox y el estado de descarga del sondeo aún no está disponible.",
|
||||||
|
"game_added_to_favorites": "Juego añadido a favoritos",
|
||||||
|
"game_removed_from_favorites": "Juego removido de favoritos"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Activar Hydra",
|
"title": "Activar Hydra",
|
||||||
@@ -259,6 +267,12 @@
|
|||||||
"download_source_errored": "Error",
|
"download_source_errored": "Error",
|
||||||
"sync_download_sources": "Sincronizar fuentes",
|
"sync_download_sources": "Sincronizar fuentes",
|
||||||
"removed_download_source": "Fuente de descarga eliminada",
|
"removed_download_source": "Fuente de descarga eliminada",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "No",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Sí, eliminar todo",
|
||||||
|
"description_confirmation_delete_all_sources": "Eliminarás todas las fuentes de descarga",
|
||||||
|
"title_confirmation_delete_all_sources": "Eliminar todas las fuentes de descarga",
|
||||||
|
"removed_download_sources": "Fuentes de descarga eliminadas",
|
||||||
|
"button_delete_all_sources": "Eliminar todas las fuentes de descarga",
|
||||||
"added_download_source": "Fuente de descarga añadida",
|
"added_download_source": "Fuente de descarga añadida",
|
||||||
"download_sources_synced": "Todas las fuentes de descargas están actualizadas.",
|
"download_sources_synced": "Todas las fuentes de descargas están actualizadas.",
|
||||||
"insert_valid_json_url": "Introduce una URL JSON válida",
|
"insert_valid_json_url": "Introduce una URL JSON válida",
|
||||||
@@ -290,14 +304,45 @@
|
|||||||
"no_email_account": "No has configurado un correo aún",
|
"no_email_account": "No has configurado un correo aún",
|
||||||
"no_subscription": "Disfruta Hydra de la mejor manera",
|
"no_subscription": "Disfruta Hydra de la mejor manera",
|
||||||
"no_users_blocked": "No tienes usuarios bloqueados",
|
"no_users_blocked": "No tienes usuarios bloqueados",
|
||||||
"notifications": "Notificaciones",
|
|
||||||
"renew_subscription": "Renovar Hydra Cloud",
|
"renew_subscription": "Renovar Hydra Cloud",
|
||||||
"subscription_active_until": "Tu Hydra Cloud está activa hasta {{date}}",
|
"subscription_active_until": "Tu Hydra Cloud está activa hasta {{date}}",
|
||||||
"subscription_expired_at": "Tú suscripción expiró el {{date}}",
|
"subscription_expired_at": "Tú suscripción expiró el {{date}}",
|
||||||
"subscription_renew_cancelled": "Está desactivada la renovación automática",
|
"subscription_renew_cancelled": "Está desactivada la renovación automática",
|
||||||
"subscription_renews_on": "Tú suscripción se renueva el {{date}}",
|
"subscription_renews_on": "Tú suscripción se renueva el {{date}}",
|
||||||
"update_email": "Actualizar correo",
|
"update_email": "Actualizar correo",
|
||||||
"update_password": "Actualizar contraseña"
|
"update_password": "Actualizar contraseña",
|
||||||
|
"appearance": "Apariencia",
|
||||||
|
"become_subscriber": "Sé Hydra Cloud",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"clear_themes": "Limpiar",
|
||||||
|
"create_theme": "Crear",
|
||||||
|
"create_theme_modal_description": "Crea un nuevo tema para personalizar la apariencia de Hydra",
|
||||||
|
"create_theme_modal_title": "Crear tema personalizado",
|
||||||
|
"delete_all_themes": "Eliminar todos los temas",
|
||||||
|
"delete_all_themes_description": "Esto eliminará todos tus temas personalizados",
|
||||||
|
"delete_theme": "Eliminar tema",
|
||||||
|
"delete_theme_description": "Esto eliminará el tema {{theme}}",
|
||||||
|
"edit_theme": "Editar tema",
|
||||||
|
"editor_tab_code": "Código",
|
||||||
|
"editor_tab_info": "Info",
|
||||||
|
"editor_tab_save": "Guardar",
|
||||||
|
"enable_torbox": "Habilitar Torbox",
|
||||||
|
"error_importing_theme": "Error al importar el tema",
|
||||||
|
"import_theme": "Importar tema",
|
||||||
|
"import_theme_description": "Vas a importar el tema {{theme}} desde la tienda de temas",
|
||||||
|
"insert_theme_name": "Introducí el nombre del tema",
|
||||||
|
"name_min_length": "El tema tiene que tener 3 carácteres de largo mínimo",
|
||||||
|
"no_themes": "Parece que no tenés ningún tema aún, pero no te preocupes, presiona acá para crear tu primer tema.",
|
||||||
|
"real_debrid_account_linked": "Cuenta de Real-Debrid vinculada",
|
||||||
|
"set_theme": "Establecer tema",
|
||||||
|
"theme_imported": "Tema importado exitosamente",
|
||||||
|
"theme_name": "Nombre",
|
||||||
|
"torbox_account_linked": "Cuenta de TorBox vinculada",
|
||||||
|
"torbox_description": "TorBox es tu servicio premium de seedbox que rivaliza incluso a los mejores servidores del mercado.",
|
||||||
|
"unset_theme": "Desactivar tema",
|
||||||
|
"web_store": "Tienda Web",
|
||||||
|
"enable_friend_request_notifications": "Cuando se recibe una solicitud de amistad",
|
||||||
|
"enable_auto_install": "Descargar actualizaciones automáticamente"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Descarga completada",
|
"download_complete": "Descarga completada",
|
||||||
@@ -308,13 +353,17 @@
|
|||||||
"new_update_available": "Version {{version}} disponible",
|
"new_update_available": "Version {{version}} disponible",
|
||||||
"restart_to_install_update": "Reinicia Hydra para instalar la actualización",
|
"restart_to_install_update": "Reinicia Hydra para instalar la actualización",
|
||||||
"notification_achievement_unlocked_title": "Logro desbloqueado de {{game}}",
|
"notification_achievement_unlocked_title": "Logro desbloqueado de {{game}}",
|
||||||
"notification_achievement_unlocked_body": "{{achievement}} y otros {{count}} fueron desbloqueados"
|
"notification_achievement_unlocked_body": "{{achievement}} y otros {{count}} fueron desbloqueados",
|
||||||
|
"new_friend_request_title": "Nueva solicitud de amistad",
|
||||||
|
"new_friend_request_description": "Has recibido una nueva solicitud de amistad"
|
||||||
},
|
},
|
||||||
"system_tray": {
|
"system_tray": {
|
||||||
"open": "Abrir Hydra",
|
"open": "Abrir Hydra",
|
||||||
"quit": "Salir"
|
"quit": "Salir"
|
||||||
},
|
},
|
||||||
"game_card": {
|
"game_card": {
|
||||||
|
"available_one": "Disponible",
|
||||||
|
"available_other": "Disponibles",
|
||||||
"no_downloads": "No hay descargas disponibles"
|
"no_downloads": "No hay descargas disponibles"
|
||||||
},
|
},
|
||||||
"binary_not_found_modal": {
|
"binary_not_found_modal": {
|
||||||
|
|||||||
@@ -236,6 +236,12 @@
|
|||||||
"download_source_errored": "Vigane",
|
"download_source_errored": "Vigane",
|
||||||
"sync_download_sources": "Sünkroniseeri allikad",
|
"sync_download_sources": "Sünkroniseeri allikad",
|
||||||
"removed_download_source": "Allalaadimise allikas eemaldatud",
|
"removed_download_source": "Allalaadimise allikas eemaldatud",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Ei",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Jah, kustuta kõik",
|
||||||
|
"description_confirmation_delete_all_sources": "Kustutate kõik allalaadimisallikad",
|
||||||
|
"title_confirmation_delete_all_sources": "Kustutage kõik allalaadimisallikad",
|
||||||
|
"removed_download_sources": "Allalaadimise allikas eemaldati",
|
||||||
|
"button_delete_all_sources": "Eemaldage kõik allalaadimisallikad",
|
||||||
"added_download_source": "Allalaadimise allikas lisatud",
|
"added_download_source": "Allalaadimise allikas lisatud",
|
||||||
"download_sources_synced": "Kõik allalaadimise allikad on sünkroniseeritud",
|
"download_sources_synced": "Kõik allalaadimise allikad on sünkroniseeritud",
|
||||||
"insert_valid_json_url": "Sisesta kehtiv JSON url",
|
"insert_valid_json_url": "Sisesta kehtiv JSON url",
|
||||||
|
|||||||
@@ -111,6 +111,12 @@
|
|||||||
"launch_with_system": "زمانی که سیستم روشن میشود، هایدرا را باز کن",
|
"launch_with_system": "زمانی که سیستم روشن میشود، هایدرا را باز کن",
|
||||||
"general": "کلی",
|
"general": "کلی",
|
||||||
"behavior": "رفتار",
|
"behavior": "رفتار",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "خیر",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "ہاں، سب کچھ حذف کر دیں۔",
|
||||||
|
"description_confirmation_delete_all_sources": "آپ ڈاؤن لوڈ کے تمام ذرائع کو حذف کر دیں گے۔",
|
||||||
|
"title_confirmation_delete_all_sources": "تمام منابع دانلود را حذف کنید",
|
||||||
|
"removed_download_sources": "منابع دانلود حذف شد",
|
||||||
|
"button_delete_all_sources": "تمام منابع دانلود را حذف کنید",
|
||||||
"enable_real_debrid": "فعالسازی Real-Debrid",
|
"enable_real_debrid": "فعالسازی Real-Debrid",
|
||||||
"debrid_api_token_hint": "کلید API خود را از <ب0>اینجا</0> بگیرید.",
|
"debrid_api_token_hint": "کلید API خود را از <ب0>اینجا</0> بگیرید.",
|
||||||
"save_changes": "ذخیره تغییرات"
|
"save_changes": "ذخیره تغییرات"
|
||||||
|
|||||||
@@ -99,7 +99,13 @@
|
|||||||
"notifications": "Notifications",
|
"notifications": "Notifications",
|
||||||
"enable_download_notifications": "Quand un téléchargement est terminé",
|
"enable_download_notifications": "Quand un téléchargement est terminé",
|
||||||
"enable_repack_list_notifications": "Quand un nouveau repack est ajouté",
|
"enable_repack_list_notifications": "Quand un nouveau repack est ajouté",
|
||||||
"language": "Langue"
|
"language": "Langue",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Non",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Oui, tout supprimer",
|
||||||
|
"description_confirmation_delete_all_sources": "Vous supprimerez toutes les sources de téléchargement",
|
||||||
|
"title_confirmation_delete_all_sources": "Supprimer toutes les sources de téléchargement",
|
||||||
|
"removed_download_sources": "Sources de téléchargement supprimées",
|
||||||
|
"button_delete_all_sources": "Supprimer toutes les sources de téléchargement"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Téléchargement terminé",
|
"download_complete": "Téléchargement terminé",
|
||||||
|
|||||||
@@ -106,6 +106,12 @@
|
|||||||
"change": "Frissítés",
|
"change": "Frissítés",
|
||||||
"notifications": "Értesítések",
|
"notifications": "Értesítések",
|
||||||
"enable_download_notifications": "Amikor egy letöltés befejeződik",
|
"enable_download_notifications": "Amikor egy letöltés befejeződik",
|
||||||
|
"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"
|
"enable_repack_list_notifications": "Amikor egy új repack hozzáadásra kerül"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
|
|||||||
@@ -175,6 +175,12 @@
|
|||||||
"download_sources_description": "Hydra akan mencari link unduhan dari sini. URL harus menuju file .json dengan link unduhan.",
|
"download_sources_description": "Hydra akan mencari link unduhan dari sini. URL harus menuju file .json dengan link unduhan.",
|
||||||
"validate_download_source": "Validasi",
|
"validate_download_source": "Validasi",
|
||||||
"remove_download_source": "Hapus",
|
"remove_download_source": "Hapus",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "TIDAK",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Ya, hapus semuanya",
|
||||||
|
"description_confirmation_delete_all_sources": "Anda akan menghapus semua sumber unduhan",
|
||||||
|
"title_confirmation_delete_all_sources": "Hapus semua sumber unduhan",
|
||||||
|
"removed_download_sources": "Font dihapus",
|
||||||
|
"button_delete_all_sources": "Hapus semua sumber unduhan",
|
||||||
"add_download_source": "Tambahkan sumber",
|
"add_download_source": "Tambahkan sumber",
|
||||||
"download_count_zero": "Tidak ada unduhan dalam daftar",
|
"download_count_zero": "Tidak ada unduhan dalam daftar",
|
||||||
"download_count_one": "{{countFormatted}} unduhan dalam daftar",
|
"download_count_one": "{{countFormatted}} unduhan dalam daftar",
|
||||||
|
|||||||
@@ -120,6 +120,12 @@
|
|||||||
"general": "Generale",
|
"general": "Generale",
|
||||||
"behavior": "Comportamento",
|
"behavior": "Comportamento",
|
||||||
"enable_real_debrid": "Abilita Real Debrid",
|
"enable_real_debrid": "Abilita Real Debrid",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "NO",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Sì, cancella tutto",
|
||||||
|
"description_confirmation_delete_all_sources": "Eliminerai tutte le fonti di download",
|
||||||
|
"title_confirmation_delete_all_sources": "Elimina tutte le fonti di download",
|
||||||
|
"removed_download_sources": "Fonti di download rimosse",
|
||||||
|
"button_delete_all_sources": "Rimuovi tutte le fonti di download",
|
||||||
"debrid_api_token_hint": "Puoi trovare la tua chiave API <0>here</0>",
|
"debrid_api_token_hint": "Puoi trovare la tua chiave API <0>here</0>",
|
||||||
"save_changes": "Salva modifiche"
|
"save_changes": "Salva modifiche"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -173,6 +173,12 @@
|
|||||||
"download_sources_description": "Hydra осы көздерден жүктеу сілтемелерін алады. URL-да жүктеу сілтемелері бар .json файлына тікелей сілтеме болуы керек.",
|
"download_sources_description": "Hydra осы көздерден жүктеу сілтемелерін алады. URL-да жүктеу сілтемелері бар .json файлына тікелей сілтеме болуы керек.",
|
||||||
"validate_download_source": "Тексеру",
|
"validate_download_source": "Тексеру",
|
||||||
"remove_download_source": "Жою",
|
"remove_download_source": "Жою",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Жоқ",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Иә, бәрін жойыңыз",
|
||||||
|
"description_confirmation_delete_all_sources": "Барлық жүктеу көздерін жоясыз",
|
||||||
|
"title_confirmation_delete_all_sources": "Барлық жүктеу көздерін жойыңыз",
|
||||||
|
"removed_download_sources": "Қаріптер жойылды",
|
||||||
|
"button_delete_all_sources": "Барлық жүктеу көздерін жойыңыз",
|
||||||
"add_download_source": "Жүктеу көзін қосу",
|
"add_download_source": "Жүктеу көзін қосу",
|
||||||
"download_count_zero": "Жүктеулер тізімінде жоқ",
|
"download_count_zero": "Жүктеулер тізімінде жоқ",
|
||||||
"download_count_one": "{{countFormatted}} жүктеу тізімде",
|
"download_count_one": "{{countFormatted}} жүктеу тізімде",
|
||||||
|
|||||||
@@ -111,6 +111,12 @@
|
|||||||
"launch_with_system": "컴퓨터가 시작되었을 때 Hydra 실행",
|
"launch_with_system": "컴퓨터가 시작되었을 때 Hydra 실행",
|
||||||
"general": "일반",
|
"general": "일반",
|
||||||
"behavior": "행동",
|
"behavior": "행동",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "아니요",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "네, 모두 삭제합니다",
|
||||||
|
"description_confirmation_delete_all_sources": "모든 다운로드 소스를 삭제합니다.",
|
||||||
|
"title_confirmation_delete_all_sources": "모든 다운로드 소스 삭제",
|
||||||
|
"removed_download_sources": "제거된 글꼴",
|
||||||
|
"button_delete_all_sources": "모든 다운로드 소스 제거",
|
||||||
"enable_real_debrid": "Real-Debrid 활성화",
|
"enable_real_debrid": "Real-Debrid 활성화",
|
||||||
"debrid_api_token_hint": "API 키를 <0>이곳</0>에서 얻으세요.",
|
"debrid_api_token_hint": "API 키를 <0>이곳</0>에서 얻으세요.",
|
||||||
"save_changes": "변경 사항 저장"
|
"save_changes": "변경 사항 저장"
|
||||||
|
|||||||
@@ -201,6 +201,12 @@
|
|||||||
"download_source_errored": "Mislyktes",
|
"download_source_errored": "Mislyktes",
|
||||||
"sync_download_sources": "Synkroniser kilder",
|
"sync_download_sources": "Synkroniser kilder",
|
||||||
"removed_download_source": "Nedlastingskilde fjernet",
|
"removed_download_source": "Nedlastingskilde fjernet",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Ingen",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Ja, slett alt",
|
||||||
|
"description_confirmation_delete_all_sources": "Du vil slette alle nedlastingskilder",
|
||||||
|
"title_confirmation_delete_all_sources": "Slett alle nedlastingskilder",
|
||||||
|
"removed_download_sources": "Fonter fjernet",
|
||||||
|
"button_delete_all_sources": "Fjern alle nedlastingskilder",
|
||||||
"added_download_source": "La til Nedlastingskilde",
|
"added_download_source": "La til Nedlastingskilde",
|
||||||
"download_sources_synced": "Alle nedlastingskilder er synkroniserte",
|
"download_sources_synced": "Alle nedlastingskilder er synkroniserte",
|
||||||
"insert_valid_json_url": "Innsett en gyldig JSON url",
|
"insert_valid_json_url": "Innsett en gyldig JSON url",
|
||||||
|
|||||||
@@ -112,6 +112,12 @@
|
|||||||
"launch_with_system": "Start Hydra bij het opstarten van het systeem",
|
"launch_with_system": "Start Hydra bij het opstarten van het systeem",
|
||||||
"general": "Algemeen",
|
"general": "Algemeen",
|
||||||
"behavior": "Gedrag",
|
"behavior": "Gedrag",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Nee",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Ja, verwijder alles",
|
||||||
|
"description_confirmation_delete_all_sources": "Je verwijdert alle downloadbronnen",
|
||||||
|
"title_confirmation_delete_all_sources": "Verwijder alle downloadbronnen",
|
||||||
|
"removed_download_sources": "Downloadbronnen verwijderd",
|
||||||
|
"button_delete_all_sources": "Verwijder alle downloadbronnen",
|
||||||
"enable_real_debrid": "Enable Real-Debrid",
|
"enable_real_debrid": "Enable Real-Debrid",
|
||||||
"debrid_api_token_hint": "U kunt uw API-sleutel <0>hier</0> verkrijgen.",
|
"debrid_api_token_hint": "U kunt uw API-sleutel <0>hier</0> verkrijgen.",
|
||||||
"save_changes": "Wijzigingen opslaan"
|
"save_changes": "Wijzigingen opslaan"
|
||||||
|
|||||||
@@ -120,6 +120,11 @@
|
|||||||
"general": "Ogólne",
|
"general": "Ogólne",
|
||||||
"behavior": "Zachowania",
|
"behavior": "Zachowania",
|
||||||
"language": "Język",
|
"language": "Język",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "NIE",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Tak, usuń wszystko",
|
||||||
|
"description_confirmation_delete_all_sources": "Usuniesz wszystkie źródła pobierania",
|
||||||
|
"title_confirmation_delete_all_sources": "Usuń wszystkie źródła pobierania",
|
||||||
|
"button_delete_all_sources": "Usuń wszystkie źródła pobierania",
|
||||||
"enable_real_debrid": "Włącz Real-Debrid",
|
"enable_real_debrid": "Włącz Real-Debrid",
|
||||||
"debrid_api_token_hint": "Możesz uzyskać swój klucz API <0>tutaj</0>",
|
"debrid_api_token_hint": "Możesz uzyskać swój klucz API <0>tutaj</0>",
|
||||||
"save_changes": "Zapisz zmiany"
|
"save_changes": "Zapisz zmiany"
|
||||||
|
|||||||
@@ -31,7 +31,6 @@
|
|||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"search": "Buscar jogos",
|
"search": "Buscar jogos",
|
||||||
|
|
||||||
"catalogue": "Catálogo",
|
"catalogue": "Catálogo",
|
||||||
"downloads": "Downloads",
|
"downloads": "Downloads",
|
||||||
"search_results": "Resultados da busca",
|
"search_results": "Resultados da busca",
|
||||||
@@ -165,6 +164,8 @@
|
|||||||
"max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo",
|
"max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo",
|
||||||
"achievements_not_sync": "Veja como exibir suas conquistas no perfil",
|
"achievements_not_sync": "Veja como exibir suas conquistas no perfil",
|
||||||
"backup_from": "Backup de {{date}}",
|
"backup_from": "Backup de {{date}}",
|
||||||
|
"automatic_backup_from": "Backup automático de {{date}}",
|
||||||
|
"enable_automatic_cloud_sync": "Habilitar sincronização automática na nuvem",
|
||||||
"custom_backup_location_set": "Localização customizada selecionada",
|
"custom_backup_location_set": "Localização customizada selecionada",
|
||||||
"select_folder": "Selecione a pasta",
|
"select_folder": "Selecione a pasta",
|
||||||
"manage_files_description": "Gerencie quais arquivos serão feitos backup",
|
"manage_files_description": "Gerencie quais arquivos serão feitos backup",
|
||||||
@@ -255,6 +256,12 @@
|
|||||||
"download_source_errored": "Falhou",
|
"download_source_errored": "Falhou",
|
||||||
"sync_download_sources": "Sincronizar",
|
"sync_download_sources": "Sincronizar",
|
||||||
"removed_download_source": "Fonte removida",
|
"removed_download_source": "Fonte removida",
|
||||||
|
"removed_download_sources": "Fontes removidas",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Não",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Sim, excluir tudo",
|
||||||
|
"title_confirmation_delete_all_sources": "Remover todas as fontes de download",
|
||||||
|
"description_confirmation_delete_all_sources": "Você irá remover todas as fontes de download. Deseja prosseguir?",
|
||||||
|
"button_delete_all_sources": "Remover todas",
|
||||||
"added_download_source": "Fonte adicionada",
|
"added_download_source": "Fonte adicionada",
|
||||||
"download_sources_synced": "As fontes foram sincronizadas",
|
"download_sources_synced": "As fontes foram sincronizadas",
|
||||||
"insert_valid_json_url": "Insira a url de um JSON válido",
|
"insert_valid_json_url": "Insira a url de um JSON válido",
|
||||||
@@ -315,12 +322,16 @@
|
|||||||
"enable_torbox": "Habilitar Torbox",
|
"enable_torbox": "Habilitar Torbox",
|
||||||
"torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.",
|
"torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.",
|
||||||
"torbox_account_linked": "Conta do TorBox vinculada",
|
"torbox_account_linked": "Conta do TorBox vinculada",
|
||||||
|
"create_real_debrid_account": "Clique aqui se você ainda não tem uma conta do Real-Debrid",
|
||||||
|
"create_torbox_account": "Clique aqui se você ainda não tem uma conta do TorBox",
|
||||||
"real_debrid_account_linked": "Conta Real-Debrid associada",
|
"real_debrid_account_linked": "Conta Real-Debrid associada",
|
||||||
"name_min_length": "O nome do tema deve ter pelo menos 3 caracteres",
|
"name_min_length": "O nome do tema deve ter pelo menos 3 caracteres",
|
||||||
"import_theme": "Importar tema",
|
"import_theme": "Importar tema",
|
||||||
"import_theme_description": "Você irá importar {{theme}} da loja de temas",
|
"import_theme_description": "Você irá importar {{theme}} da loja de temas",
|
||||||
"error_importing_theme": "Erro ao importar tema",
|
"error_importing_theme": "Erro ao importar tema",
|
||||||
"theme_imported": "Tema importado com sucesso"
|
"theme_imported": "Tema importado com sucesso",
|
||||||
|
"enable_friend_request_notifications": "Quando um pedido de amizade é recebido",
|
||||||
|
"enable_auto_install": "Baixar atualizações automaticamente"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Download concluído",
|
"download_complete": "Download concluído",
|
||||||
@@ -329,13 +340,17 @@
|
|||||||
"repack_count_one": "{{count}} novo repack",
|
"repack_count_one": "{{count}} novo repack",
|
||||||
"repack_count_other": "{{count}} novos repacks",
|
"repack_count_other": "{{count}} novos repacks",
|
||||||
"new_update_available": "Versão {{version}} disponível",
|
"new_update_available": "Versão {{version}} disponível",
|
||||||
"restart_to_install_update": "Reinicie o Hydra para instalar a nova versão"
|
"restart_to_install_update": "Reinicie o Hydra para instalar a nova versão",
|
||||||
|
"new_friend_request_title": "Novo pedido de amizade",
|
||||||
|
"new_friend_request_description": "Você recebeu um novo pedido de amizade"
|
||||||
},
|
},
|
||||||
"system_tray": {
|
"system_tray": {
|
||||||
"open": "Abrir Hydra",
|
"open": "Abrir Hydra",
|
||||||
"quit": "Fechar"
|
"quit": "Fechar"
|
||||||
},
|
},
|
||||||
"game_card": {
|
"game_card": {
|
||||||
|
"available_one": "Disponível",
|
||||||
|
"available_other": "Disponíveis",
|
||||||
"no_downloads": "Sem downloads disponíveis"
|
"no_downloads": "Sem downloads disponíveis"
|
||||||
},
|
},
|
||||||
"binary_not_found_modal": {
|
"binary_not_found_modal": {
|
||||||
@@ -440,9 +455,6 @@
|
|||||||
"show_achievements_on_profile": "Exiba suas conquistas no perfil",
|
"show_achievements_on_profile": "Exiba suas conquistas no perfil",
|
||||||
"show_points_on_profile": "Exiba seus pontos ganhos no perfil"
|
"show_points_on_profile": "Exiba seus pontos ganhos no perfil"
|
||||||
},
|
},
|
||||||
"badge": {
|
|
||||||
"badge_description_theme_creator": "Concedido àqueles que criaram um tema customizado"
|
|
||||||
},
|
|
||||||
"achievement": {
|
"achievement": {
|
||||||
"achievement_unlocked": "Conquista desbloqueada",
|
"achievement_unlocked": "Conquista desbloqueada",
|
||||||
"your_achievements": "Suas Conquistas",
|
"your_achievements": "Suas Conquistas",
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"search": "Procurar jogos",
|
"search": "Procurar jogos",
|
||||||
|
|
||||||
"catalogue": "Catálogo",
|
"catalogue": "Catálogo",
|
||||||
"downloads": "Transferências",
|
"downloads": "Transferências",
|
||||||
"search_results": "Resultados da pesquisa",
|
"search_results": "Resultados da pesquisa",
|
||||||
@@ -103,7 +102,7 @@
|
|||||||
"open_download_location": "Ver ficheiros transferidos",
|
"open_download_location": "Ver ficheiros transferidos",
|
||||||
"create_shortcut": "Criar atalho no ambiente de trabalho",
|
"create_shortcut": "Criar atalho no ambiente de trabalho",
|
||||||
"remove_files": "Remover ficheiros",
|
"remove_files": "Remover ficheiros",
|
||||||
"options": "Gerir",
|
"options": "Opções",
|
||||||
"remove_from_library_description": "Isto vai remover {{game}} da tua biblioteca",
|
"remove_from_library_description": "Isto vai remover {{game}} da tua biblioteca",
|
||||||
"remove_from_library_title": "Tens a certeza?",
|
"remove_from_library_title": "Tens a certeza?",
|
||||||
"executable_section_title": "Executável",
|
"executable_section_title": "Executável",
|
||||||
@@ -159,7 +158,27 @@
|
|||||||
"no_download_option_info": "Sem informações disponíveis",
|
"no_download_option_info": "Sem informações disponíveis",
|
||||||
"backup_deletion_failed": "Falha ao apagar o backup",
|
"backup_deletion_failed": "Falha ao apagar o backup",
|
||||||
"max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo",
|
"max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo",
|
||||||
"achievements_not_sync": "As tuas conquistas não estão sincronizadas"
|
"achievements_not_sync": "As tuas conquistas não estão sincronizadas",
|
||||||
|
"backup_from": "Backup de {{date}}",
|
||||||
|
"automatic_backup_from": "Backup automático de {{date}}",
|
||||||
|
"enable_automatic_cloud_sync": "Ativar sincronização automática na nuvem",
|
||||||
|
"custom_backup_location_set": "Localização customizada selecionada",
|
||||||
|
"select_folder": "Selecione a pasta",
|
||||||
|
"manage_files_description": "Gerencie quais arquivos serão feitos backup",
|
||||||
|
"clear": "Limpar",
|
||||||
|
"no_directory_selected": "Nenhum diretório selecionado",
|
||||||
|
"reset_achievements": "Repor conquistas",
|
||||||
|
"reset_achievements_description": "Isto irá apagar todas as conquistas de {{game}}",
|
||||||
|
"reset_achievements_title": "Tem certeza?",
|
||||||
|
"reset_achievements_success": "Conquistas repostas com sucesso",
|
||||||
|
"reset_achievements_error": "Falha ao repor conquistas",
|
||||||
|
"no_write_permission": "Não é possível descarregar neste diretório. Clique aqui para saber mais.",
|
||||||
|
"download_error_gofile_quota_exceeded": "Você excedeu sua cota mensal do Gofile. Por favor, aguarde o reset da cota.",
|
||||||
|
"download_error_real_debrid_account_not_authorized": "A sua conta do Real-Debrid não está autorizada a fazer novos downloads. Por favor, verifique a sua assinatura e tente novamente.",
|
||||||
|
"download_error_not_cached_in_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.",
|
||||||
|
"download_error_not_cached_in_torbox": "Este download não está disponível no Torbox e a verificação do status do download não está disponível.",
|
||||||
|
"game_removed_from_favorites": "Jogo removido dos favoritos",
|
||||||
|
"game_added_to_favorites": "Jogo adicionado aos favoritos"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Ativação",
|
"title": "Ativação",
|
||||||
@@ -192,7 +211,11 @@
|
|||||||
"queued": "Na fila",
|
"queued": "Na fila",
|
||||||
"no_downloads_title": "Nada por aqui…",
|
"no_downloads_title": "Nada por aqui…",
|
||||||
"no_downloads_description": "Ainda não descarregaste nada pelo Hydra, mas nunca é tarde para começar.",
|
"no_downloads_description": "Ainda não descarregaste nada pelo Hydra, mas nunca é tarde para começar.",
|
||||||
"checking_files": "A verificar ficheiros…"
|
"checking_files": "A verificar ficheiros…",
|
||||||
|
"seeding": "A semear",
|
||||||
|
"stop_seeding": "Parar de semear",
|
||||||
|
"resume_seeding": "Semear",
|
||||||
|
"options": "Opções"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"downloads_path": "Local das transferências",
|
"downloads_path": "Local das transferências",
|
||||||
@@ -232,6 +255,12 @@
|
|||||||
"download_source_errored": "Falhou",
|
"download_source_errored": "Falhou",
|
||||||
"sync_download_sources": "Sincronizar",
|
"sync_download_sources": "Sincronizar",
|
||||||
"removed_download_source": "Fonte removida",
|
"removed_download_source": "Fonte removida",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Não",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Sim, apague tudo",
|
||||||
|
"description_confirmation_delete_all_sources": "Irá remover todas as fontes de download",
|
||||||
|
"title_confirmation_delete_all_sources": "Remover todas as fontes de download",
|
||||||
|
"removed_download_sources": "Fontes de download removidas",
|
||||||
|
"button_delete_all_sources": "Remover todas",
|
||||||
"added_download_source": "Fonte adicionada",
|
"added_download_source": "Fonte adicionada",
|
||||||
"download_sources_synced": "As fontes foram sincronizadas",
|
"download_sources_synced": "As fontes foram sincronizadas",
|
||||||
"insert_valid_json_url": "Insere o URL de um JSON válido",
|
"insert_valid_json_url": "Insere o URL de um JSON válido",
|
||||||
@@ -250,7 +279,57 @@
|
|||||||
"must_be_valid_url": "A fonte deve ser um URL válido",
|
"must_be_valid_url": "A fonte deve ser um URL válido",
|
||||||
"blocked_users": "Utilizadores bloqueados",
|
"blocked_users": "Utilizadores bloqueados",
|
||||||
"user_unblocked": "Utilizador desbloqueado",
|
"user_unblocked": "Utilizador desbloqueado",
|
||||||
"enable_achievement_notifications": "Quando uma conquista é desbloqueada"
|
"enable_achievement_notifications": "Quando uma conquista é desbloqueada",
|
||||||
|
"enable_friend_request_notifications": "Quando um pedido de amizade é recebido",
|
||||||
|
"launch_minimized": "Iniciar Hydra minimizado",
|
||||||
|
"disable_nsfw_alert": "Desativar alertas NSFW",
|
||||||
|
"seed_after_download_complete": "Semear após concluir o download",
|
||||||
|
"show_hidden_achievement_description": "Mostrar descrição de conquistas ocultas antes de as desbloquear",
|
||||||
|
"account": "Conta",
|
||||||
|
"no_users_blocked": "Não tem utilizadores bloqueados",
|
||||||
|
"subscription_active_until": "O teu Hydra Cloud está ativo até {{date}}",
|
||||||
|
"manage_subscription": "Gerir subscrições",
|
||||||
|
"update_email": "Atualizar email",
|
||||||
|
"update_password": "Atualizar password",
|
||||||
|
"current_email": "Email atual:",
|
||||||
|
"no_email_account": "Ainda não adicionou nenhum email à sua conta",
|
||||||
|
"account_data_updated_successfully": "Dados da conta atualizados com sucesso",
|
||||||
|
"renew_subscription": "Renovar Hydra Cloud",
|
||||||
|
"subscription_expired_at": "A sua subscrição expirou a {{date}}",
|
||||||
|
"no_subscription": "Aproveite o Hydra da melhor forma possível",
|
||||||
|
"become_subscriber": "Subscreva o Hydra Cloud",
|
||||||
|
"subscription_renew_cancelled": "A renovação automática está desativada",
|
||||||
|
"subscription_renews_on": "Sua assinatura renova dia {{date}}",
|
||||||
|
"bill_sent_until": "A próxima cobrança será enviada até esse dia",
|
||||||
|
"no_themes": "Parece que ainda não tem nenhum tema. Não se preocupe, clique aqui para criar a sua primeira obra de arte.",
|
||||||
|
"editor_tab_code": "Código",
|
||||||
|
"editor_tab_info": "Info",
|
||||||
|
"editor_tab_save": "Guardar",
|
||||||
|
"web_store": "Loja de temas",
|
||||||
|
"clear_themes": "Limpar",
|
||||||
|
"create_theme": "Criar",
|
||||||
|
"create_theme_modal_title": "Criar tema customizado",
|
||||||
|
"create_theme_modal_description": "Criar um novo tema para customizar a aparência do Hydra",
|
||||||
|
"theme_name": "Nome",
|
||||||
|
"insert_theme_name": "Insira o nome do tema",
|
||||||
|
"set_theme": "Definir tema",
|
||||||
|
"unset_theme": "Redefinir tema",
|
||||||
|
"delete_theme": "Apagar tema",
|
||||||
|
"edit_theme": "Editar tema",
|
||||||
|
"delete_all_themes": "Apagar todos os temas",
|
||||||
|
"delete_all_themes_description": "Isto irá apagar todos os seus temas",
|
||||||
|
"delete_theme_description": "Isto irá apagar o tema {{theme}}",
|
||||||
|
"cancel": "Cancelar",
|
||||||
|
"appearance": "Aparência",
|
||||||
|
"enable_torbox": "Ativar Torbox",
|
||||||
|
"torbox_description": "TorBox é um serviço de seedbox premium sendo um dos melhores servidores do mercado.",
|
||||||
|
"torbox_account_linked": "Conta do TorBox associada",
|
||||||
|
"real_debrid_account_linked": "Conta Real-Debrid associada",
|
||||||
|
"name_min_length": "O nome do tema deve ter pelo menos 3 caracteres",
|
||||||
|
"import_theme": "Importar tema",
|
||||||
|
"import_theme_description": "Irá importar {{theme}} da loja de temas",
|
||||||
|
"error_importing_theme": "Erro ao importar tema",
|
||||||
|
"theme_imported": "Tema importado com sucesso"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Transferência concluída",
|
"download_complete": "Transferência concluída",
|
||||||
@@ -259,13 +338,17 @@
|
|||||||
"repack_count_one": "{{count}} novo repack",
|
"repack_count_one": "{{count}} novo repack",
|
||||||
"repack_count_other": "{{count}} novos repacks",
|
"repack_count_other": "{{count}} novos repacks",
|
||||||
"new_update_available": "Versão {{version}} disponível",
|
"new_update_available": "Versão {{version}} disponível",
|
||||||
"restart_to_install_update": "Reinicia o Hydra para instalar a nova versão"
|
"restart_to_install_update": "Reinicia o Hydra para instalar a nova versão",
|
||||||
|
"new_friend_request_title": "Novo pedido de amizade",
|
||||||
|
"new_friend_request_description": "Recebeste um novo pedido de amizade"
|
||||||
},
|
},
|
||||||
"system_tray": {
|
"system_tray": {
|
||||||
"open": "Abrir o Hydra",
|
"open": "Abrir o Hydra",
|
||||||
"quit": "Sair"
|
"quit": "Sair"
|
||||||
},
|
},
|
||||||
"game_card": {
|
"game_card": {
|
||||||
|
"available_one": "Disponível",
|
||||||
|
"available_other": "Disponíveis",
|
||||||
"no_downloads": "Sem downloads disponíveis"
|
"no_downloads": "Sem downloads disponíveis"
|
||||||
},
|
},
|
||||||
"binary_not_found_modal": {
|
"binary_not_found_modal": {
|
||||||
@@ -275,7 +358,16 @@
|
|||||||
},
|
},
|
||||||
"catalogue": {
|
"catalogue": {
|
||||||
"next_page": "Página seguinte",
|
"next_page": "Página seguinte",
|
||||||
"previous_page": "Página anterior"
|
"previous_page": "Página anterior",
|
||||||
|
"search": "Filtrar…",
|
||||||
|
"developers": "Desenvolvedores",
|
||||||
|
"genres": "Géneros",
|
||||||
|
"tags": "Marcadores",
|
||||||
|
"publishers": "Distribuidoras",
|
||||||
|
"download_sources": "Fontes de download",
|
||||||
|
"result_count": "{{resultCount}} resultados",
|
||||||
|
"filter_count": "{{filterCount}} disponíveis",
|
||||||
|
"clear_filters": "Limpar {{filterCount}} selecionados"
|
||||||
},
|
},
|
||||||
"modal": {
|
"modal": {
|
||||||
"close": "Botão de fechar"
|
"close": "Botão de fechar"
|
||||||
@@ -352,7 +444,17 @@
|
|||||||
"profile_reported": "Perfil denunciado",
|
"profile_reported": "Perfil denunciado",
|
||||||
"your_friend_code": "O teu código de amigo:",
|
"your_friend_code": "O teu código de amigo:",
|
||||||
"upload_banner": "Fazer upload do banner",
|
"upload_banner": "Fazer upload do banner",
|
||||||
"uploading_banner": "A fazer upload do banner…"
|
"uploading_banner": "A fazer upload do banner…",
|
||||||
|
"background_image_updated": "Imagem de fundo salva",
|
||||||
|
"stats": "Estatísticas",
|
||||||
|
"achievements": "conquistas",
|
||||||
|
"games": "Jogos",
|
||||||
|
"ranking_updated_weekly": "O ranking é atualizado semanalmente",
|
||||||
|
"playing": "A jogar {{game}}",
|
||||||
|
"achievements_unlocked": "Conquistas desbloqueadas",
|
||||||
|
"earned_points": "Pontos ganhos",
|
||||||
|
"show_achievements_on_profile": "Mostre as suas conquistas no perfil",
|
||||||
|
"show_points_on_profile": "Mostre os seus pontos ganhos no perfil"
|
||||||
},
|
},
|
||||||
"achievement": {
|
"achievement": {
|
||||||
"achievement_unlocked": "Conquista desbloqueada",
|
"achievement_unlocked": "Conquista desbloqueada",
|
||||||
@@ -360,15 +462,25 @@
|
|||||||
"user_achievements": "Conquistas de {{displayName}}",
|
"user_achievements": "Conquistas de {{displayName}}",
|
||||||
"unlocked_at": "Desbloqueada em: {{date}}",
|
"unlocked_at": "Desbloqueada em: {{date}}",
|
||||||
"subscription_needed": "Precisas de uma subscrição Hydra Cloud para visualizar este conteúdo",
|
"subscription_needed": "Precisas de uma subscrição Hydra Cloud para visualizar este conteúdo",
|
||||||
"new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos"
|
"new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos",
|
||||||
|
"achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas",
|
||||||
|
"achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}",
|
||||||
|
"hidden_achievement_tooltip": "Esta é uma conquista oculta",
|
||||||
|
"achievement_earn_points": "Ganhe {{points}} pontos com essa conquista",
|
||||||
|
"earned_points": "Pontos ganhos:",
|
||||||
|
"available_points": "Pontos disponíveis:",
|
||||||
|
"how_to_earn_achievements_points": "Como desbloquear pontos nas conquistas?"
|
||||||
},
|
},
|
||||||
"hydra_cloud": {
|
"hydra_cloud": {
|
||||||
"subscription_tour_title": "Subscrição Hydra Cloud",
|
"subscription_tour_title": "Subscrição Hydra Cloud",
|
||||||
"subscribe_now": "Subscreve agora",
|
"subscribe_now": "Subscreve agora",
|
||||||
"cloud_achievements": "Gravação de conquistas na nuvem",
|
"cloud_achievements": "Gravação de conquistas na nuvem",
|
||||||
"animated_profile_picture": "Fotos de perfil animadas",
|
"animated_profile_picture": "Fotos de perfil animadas",
|
||||||
"premium_support": "Apoio Premium",
|
"premium_support": "Suporte Premium",
|
||||||
"show_and_compare_achievements": "Mostra e compara as tuas conquistas com as de outros utilizadores",
|
"show_and_compare_achievements": "Mostra e compara as tuas conquistas com as de outros utilizadores",
|
||||||
"animated_profile_banner": "Banner animado no perfil"
|
"animated_profile_banner": "Banner animado no perfil",
|
||||||
|
"cloud_saving": "Progresso dos jogos na nuvem",
|
||||||
|
"hydra_cloud_feature_found": "Descubriste uma funcionalidade Hydra Cloud!",
|
||||||
|
"learn_more": "Saber mais"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -127,6 +127,12 @@
|
|||||||
"behavior": "Comportament",
|
"behavior": "Comportament",
|
||||||
"language": "Limbă",
|
"language": "Limbă",
|
||||||
"api_token": "Token API",
|
"api_token": "Token API",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Nu",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Da, șterge totul",
|
||||||
|
"description_confirmation_delete_all_sources": "Veți șterge toate sursele de descărcare",
|
||||||
|
"title_confirmation_delete_all_sources": "Ștergeți toate sursele de descărcare",
|
||||||
|
"removed_download_sources": "Sursele de descărcare au fost eliminate",
|
||||||
|
"button_delete_all_sources": "Eliminați toate sursele de descărcare",
|
||||||
"enable_real_debrid": "Activează Real-Debrid",
|
"enable_real_debrid": "Activează Real-Debrid",
|
||||||
"real_debrid_description": "Real-Debrid este un descărcător fără restricții care îți permite să descarci fișiere instantaneu și la cea mai bună viteză a internetului tău.",
|
"real_debrid_description": "Real-Debrid este un descărcător fără restricții care îți permite să descarci fișiere instantaneu și la cea mai bună viteză a internetului tău.",
|
||||||
"debrid_invalid_token": "Token API invalid",
|
"debrid_invalid_token": "Token API invalid",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"manage_files_description": "Управляйте файлами, которые будут сохраняться и восстанавливаться",
|
"manage_files_description": "Управляйте файлами, которые будут сохраняться и восстанавливаться",
|
||||||
"select_folder": "Выбрать папку",
|
"select_folder": "Выбрать папку",
|
||||||
"backup_from": "Резервная копия от {{date}}",
|
"backup_from": "Резервная копия от {{date}}",
|
||||||
|
"automatic_backup_from": "Автоматическая резервная копия от {{date}}",
|
||||||
|
"enable_automatic_cloud_sync": "Включить автоматическую синхронизацию в облаке",
|
||||||
"custom_backup_location_set": "Установлено настраиваемое местоположение резервной копии",
|
"custom_backup_location_set": "Установлено настраиваемое местоположение резервной копии",
|
||||||
"no_directory_selected": "Не выбран каталог",
|
"no_directory_selected": "Не выбран каталог",
|
||||||
"no_write_permission": "Невозможно загрузить в эту директорию. Нажмите здесь, чтобы узнать больше.",
|
"no_write_permission": "Невозможно загрузить в эту директорию. Нажмите здесь, чтобы узнать больше.",
|
||||||
@@ -266,6 +268,12 @@
|
|||||||
"download_source_errored": "Ошибка",
|
"download_source_errored": "Ошибка",
|
||||||
"sync_download_sources": "Обновить источники",
|
"sync_download_sources": "Обновить источники",
|
||||||
"removed_download_source": "Источник загрузок удален",
|
"removed_download_source": "Источник загрузок удален",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Нет",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Да, удалить все",
|
||||||
|
"description_confirmation_delete_all_sources": "Вы удалите все источники загрузки",
|
||||||
|
"title_confirmation_delete_all_sources": "Удалить все источники загрузки",
|
||||||
|
"removed_download_sources": "Шрифты удалены",
|
||||||
|
"button_delete_all_sources": "Удалить все источники загрузки",
|
||||||
"added_download_source": "Источник загрузок добавлен",
|
"added_download_source": "Источник загрузок добавлен",
|
||||||
"download_sources_synced": "Все источники загрузок синхронизированы",
|
"download_sources_synced": "Все источники загрузок синхронизированы",
|
||||||
"insert_valid_json_url": "Вставьте действительный URL JSON-файла",
|
"insert_valid_json_url": "Вставьте действительный URL JSON-файла",
|
||||||
@@ -330,7 +338,9 @@
|
|||||||
"import_theme": "Импортировать тему",
|
"import_theme": "Импортировать тему",
|
||||||
"import_theme_description": "Вы импортируете {{theme}} из магазина тем",
|
"import_theme_description": "Вы импортируете {{theme}} из магазина тем",
|
||||||
"error_importing_theme": "Ошибка при импорте темы",
|
"error_importing_theme": "Ошибка при импорте темы",
|
||||||
"theme_imported": "Тема успешно импортирована"
|
"theme_imported": "Тема успешно импортирована",
|
||||||
|
"enable_friend_request_notifications": "При получении запроса на добавление в друзья",
|
||||||
|
"enable_auto_install": "Загружать обновления автоматически"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Загрузка завершена",
|
"download_complete": "Загрузка завершена",
|
||||||
@@ -341,13 +351,17 @@
|
|||||||
"new_update_available": "Доступна новая версия {{version}}",
|
"new_update_available": "Доступна новая версия {{version}}",
|
||||||
"restart_to_install_update": "Перезапустите Hydra для установки обновления",
|
"restart_to_install_update": "Перезапустите Hydra для установки обновления",
|
||||||
"notification_achievement_unlocked_title": "Достижение разблокировано для {{game}}",
|
"notification_achievement_unlocked_title": "Достижение разблокировано для {{game}}",
|
||||||
"notification_achievement_unlocked_body": "были разблокированы {{achievement}} и другие {{count}}"
|
"notification_achievement_unlocked_body": "были разблокированы {{achievement}} и другие {{count}}",
|
||||||
|
"new_friend_request_title": "Новый запрос на добавление в друзья",
|
||||||
|
"new_friend_request_description": "Вы получили новый запрос на добавление в друзья"
|
||||||
},
|
},
|
||||||
"system_tray": {
|
"system_tray": {
|
||||||
"open": "Открыть Hydra",
|
"open": "Открыть Hydra",
|
||||||
"quit": "Выйти"
|
"quit": "Выйти"
|
||||||
},
|
},
|
||||||
"game_card": {
|
"game_card": {
|
||||||
|
"available_one": "Доступный",
|
||||||
|
"available_other": "Доступный",
|
||||||
"no_downloads": "Нет доступных источников"
|
"no_downloads": "Нет доступных источников"
|
||||||
},
|
},
|
||||||
"binary_not_found_modal": {
|
"binary_not_found_modal": {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"catalogue": "Katalog",
|
"catalogue": "Katalog",
|
||||||
"downloads": "İndirilenler",
|
"downloads": "İndirilenler",
|
||||||
"settings": "Ayarlar",
|
"settings": "Ayarlar",
|
||||||
"my_library": "Kütüphanem",
|
"my_library": "Kütüphane",
|
||||||
"downloading_metadata": "{{title}} (Meta verileri indiriliyor…)",
|
"downloading_metadata": "{{title}} (Meta verileri indiriliyor…)",
|
||||||
"paused": "{{title}} (Durduruldu)",
|
"paused": "{{title}} (Durduruldu)",
|
||||||
"downloading": "{{title}} ({{percentage}} - İndiriliyor…)",
|
"downloading": "{{title}} ({{percentage}} - İndiriliyor…)",
|
||||||
@@ -36,13 +36,13 @@
|
|||||||
"downloads": "İndirilenler",
|
"downloads": "İndirilenler",
|
||||||
"search_results": "Arama sonuçları",
|
"search_results": "Arama sonuçları",
|
||||||
"settings": "Ayarlar",
|
"settings": "Ayarlar",
|
||||||
"version_available_install": "Sürüm {{version}} mevcut. Yüklemek ve yeniden başlatmak için buraya tıklayın.",
|
"version_available_install": "{{version}} sürümü mevcut. Yüklemek ve yeniden başlatmak için buraya tıklayın.",
|
||||||
"version_available_download": "Sürüm {{version}} mevcut. İndirmek için buraya tıklayın."
|
"version_available_download": "{{version}} sürümü mevcut. İndirmek için buraya tıklayın."
|
||||||
},
|
},
|
||||||
"bottom_panel": {
|
"bottom_panel": {
|
||||||
"no_downloads_in_progress": "Devam eden indirme yok",
|
"no_downloads_in_progress": "Devam eden indirme yok",
|
||||||
"downloading_metadata": "{{title}} meta verileri indiriliyor…",
|
"downloading_metadata": "{{title}} meta verileri indiriliyor…",
|
||||||
"downloading": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Tamamlama: {{eta}} - Hız: {{speed}}",
|
"downloading": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Tamamlanma: {{eta}} - Hız: {{speed}}",
|
||||||
"calculating_eta": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Kalan süre hesaplanıyor…",
|
"calculating_eta": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Kalan süre hesaplanıyor…",
|
||||||
"checking_files": "{{title}} dosyaları kontrol ediliyor… ({{percentage}} tamamlandı)"
|
"checking_files": "{{title}} dosyaları kontrol ediliyor… ({{percentage}} tamamlandı)"
|
||||||
},
|
},
|
||||||
@@ -116,14 +116,14 @@
|
|||||||
"clear": "Temizle",
|
"clear": "Temizle",
|
||||||
"remove_files": "Dosyaları kaldır",
|
"remove_files": "Dosyaları kaldır",
|
||||||
"remove_from_library_title": "Emin misiniz?",
|
"remove_from_library_title": "Emin misiniz?",
|
||||||
"remove_from_library_description": "Bu işlem {{game}} oyununu kütüphanenizden kaldıracaktır",
|
"remove_from_library_description": "Bu işlem sonrasında {{game}} oyunu kütüphanenizden kaldıracaktır",
|
||||||
"options": "Seçenekler",
|
"options": "Seçenekler",
|
||||||
"executable_section_title": "Çalıştırılabilir dosya",
|
"executable_section_title": "Çalıştırılabilir dosya",
|
||||||
"executable_section_description": "\"Oyna\" tıklandığında çalıştırılacak dosyanın yolu",
|
"executable_section_description": "\"Oyna\" butonuna tıklandığında çalıştırılacak dosyanın yolu",
|
||||||
"downloads_secion_title": "İndirmeler",
|
"downloads_secion_title": "İndirmeler",
|
||||||
"downloads_section_description": "Bu oyun için güncellemeleri veya diğer sürümleri kontrol edin",
|
"downloads_section_description": "Bu oyun için güncellemeleri veya diğer sürümleri kontrol edin",
|
||||||
"danger_zone_section_title": "Tehlike bölgesi",
|
"danger_zone_section_title": "Tehlike bölgesi",
|
||||||
"danger_zone_section_description": "Bu oyunu kütüphanenizden veya Hydra tarafından indirilen dosyaları kaldırın",
|
"danger_zone_section_description": "Bu oyunu kütüphanenizden kaldırın veya Hydra tarafından indirilen dosyaları silin.",
|
||||||
"download_in_progress": "İndirme devam ediyor",
|
"download_in_progress": "İndirme devam ediyor",
|
||||||
"download_paused": "İndirme durduruldu",
|
"download_paused": "İndirme durduruldu",
|
||||||
"last_downloaded_option": "Son indirilen seçenek",
|
"last_downloaded_option": "Son indirilen seçenek",
|
||||||
@@ -141,8 +141,8 @@
|
|||||||
"executable_path_in_use": "\"{{game}}\" tarafından kullanılan çalıştırılabilir dosya",
|
"executable_path_in_use": "\"{{game}}\" tarafından kullanılan çalıştırılabilir dosya",
|
||||||
"warning": "Uyarı:",
|
"warning": "Uyarı:",
|
||||||
"hydra_needs_to_remain_open": "Bu indirmenin tamamlanması için Hydra açık kalmalıdır. Eğer Hydra kapanırsa, ilerleme kaydedilmez.",
|
"hydra_needs_to_remain_open": "Bu indirmenin tamamlanması için Hydra açık kalmalıdır. Eğer Hydra kapanırsa, ilerleme kaydedilmez.",
|
||||||
"achievements": "Başarılar",
|
"achievements": "Başarımlar",
|
||||||
"achievements_count": "Başarılar {{unlockedCount}}/{{achievementsCount}}",
|
"achievements_count": "Başarımlar {{unlockedCount}}/{{achievementsCount}}",
|
||||||
"cloud_save": "Bulut kaydı",
|
"cloud_save": "Bulut kaydı",
|
||||||
"cloud_save_description": "İlerlemenizi buluta kaydedin ve herhangi bir cihazda oynamaya devam edin",
|
"cloud_save_description": "İlerlemenizi buluta kaydedin ve herhangi bir cihazda oynamaya devam edin",
|
||||||
"backups": "Yedekler",
|
"backups": "Yedekler",
|
||||||
@@ -150,15 +150,15 @@
|
|||||||
"delete_backup": "Sil",
|
"delete_backup": "Sil",
|
||||||
"create_backup": "Yeni yedek oluştur",
|
"create_backup": "Yeni yedek oluştur",
|
||||||
"last_backup_date": "{{date}} tarihindeki son yedek",
|
"last_backup_date": "{{date}} tarihindeki son yedek",
|
||||||
"no_backup_preview": "Bu oyun için kayıtlı oyun bulunamadı",
|
"no_backup_preview": "Bu oyun için bir kayıt dosyası bulunamadı",
|
||||||
"restoring_backup": "Yedek geri yükleniyor ({{progress}} tamamlandı)…",
|
"restoring_backup": "Yedek geri yükleniyor ({{progress}} tamamlandı)…",
|
||||||
"uploading_backup": "Yedek yükleniyor…",
|
"uploading_backup": "Yedek yükleniyor…",
|
||||||
"no_backups": "Bu oyun için henüz bir yedek oluşturmadınız",
|
"no_backups": "Bu oyun için henüz bir yedek oluşturmadınız",
|
||||||
"backup_uploaded": "Yedek yüklendi",
|
"backup_uploaded": "Yedek yüklendi",
|
||||||
"backup_deleted": "Yedek silindi",
|
"backup_deleted": "Yedek silindi",
|
||||||
"backup_restored": "Yedek geri yüklendi",
|
"backup_restored": "Yedek geri yüklendi",
|
||||||
"see_all_achievements": "Tüm başarıları gör",
|
"see_all_achievements": "Tüm başarımları gör",
|
||||||
"sign_in_to_see_achievements": "Başarıları görmek için giriş yapın",
|
"sign_in_to_see_achievements": "Başarımları görmek için oturum açın",
|
||||||
"mapping_method_automatic": "Otomatik",
|
"mapping_method_automatic": "Otomatik",
|
||||||
"mapping_method_manual": "Manuel",
|
"mapping_method_manual": "Manuel",
|
||||||
"mapping_method_label": "Eşleme yöntemi",
|
"mapping_method_label": "Eşleme yöntemi",
|
||||||
@@ -169,29 +169,37 @@
|
|||||||
"wine_prefix": "Wine Prefix",
|
"wine_prefix": "Wine Prefix",
|
||||||
"wine_prefix_description": "Bu oyunu çalıştırmak için kullanılan Wine Prefix",
|
"wine_prefix_description": "Bu oyunu çalıştırmak için kullanılan Wine Prefix",
|
||||||
"launch_options": "Başlatma Seçenekleri",
|
"launch_options": "Başlatma Seçenekleri",
|
||||||
"launch_options_description": "İleri düzey kullanıcılar, başlatma seçeneklerine değişiklikler girebilir (deneysel özellik)",
|
"launch_options_description": "İleri düzey kullanıcılar, başlatma seçeneklerine parametreler girebilir (deneysel özellik)",
|
||||||
"launch_options_placeholder": "Belirtilen bir parametre yok",
|
"launch_options_placeholder": "Belirtilen bir parametre yok",
|
||||||
"no_download_option_info": "Bilgi mevcut değil",
|
"no_download_option_info": "Bilgi mevcut değil",
|
||||||
"backup_deletion_failed": "Yedek silinemedi",
|
"backup_deletion_failed": "Yedek silinemedi",
|
||||||
"max_number_of_artifacts_reached": "Bu oyun için maksimum yedek sayısına ulaşıldı",
|
"max_number_of_artifacts_reached": "Bu oyun için maksimum yedek sayısına ulaşıldı",
|
||||||
"achievements_not_sync": "Başarılarınızı senkronize etmeyi öğrenin",
|
"achievements_not_sync": "Başarımlarınızı senkronize etmeyi öğrenin",
|
||||||
"manage_files_description": "Hangi dosyaların yedeklenip geri yükleneceğini yönetin",
|
"manage_files_description": "Hangi dosyaların yedeklenip geri yükleneceğini yönetin",
|
||||||
"select_folder": "Klasör seç",
|
"select_folder": "Klasör seç",
|
||||||
"backup_from": "{{date}} tarihinden yedek",
|
"backup_from": "{{date}} tarihinden yedek",
|
||||||
|
"automatic_backup_from": "{{date}} tarihinden otomatik kayıt",
|
||||||
|
"enable_automatic_cloud_sync": "Otomatik bulut kaydı senkronizasyonunu aktifleştir",
|
||||||
"custom_backup_location_set": "Özel yedekleme konumu ayarlandı",
|
"custom_backup_location_set": "Özel yedekleme konumu ayarlandı",
|
||||||
"no_directory_selected": "Bir dizin seçilmedi",
|
"no_directory_selected": "Bir dizin seçilmedi",
|
||||||
"no_write_permission": "Bu dizine indirme yapılamaz. Daha fazla bilgi için buraya tıklayın.",
|
"no_write_permission": "Bu dizine indirme yapılamaz. Daha fazla bilgi için buraya tıklayın.",
|
||||||
"reset_achievements": "Başarıları sıfırla",
|
"reset_achievements": "Başarımları sıfırla",
|
||||||
"reset_achievements_description": "Bu işlem {{game}} için tüm başarıları sıfırlar",
|
"reset_achievements_description": "Bu işlem {{game}} için tüm başarımları sıfırlar",
|
||||||
"reset_achievements_title": "Emin misiniz?",
|
"reset_achievements_title": "Emin misiniz?",
|
||||||
"reset_achievements_success": "Başarılar başarıyla sıfırlandı",
|
"reset_achievements_success": "Başarımlar başarıyla sıfırlandı",
|
||||||
"reset_achievements_error": "Başarılar sıfırlanamadı"
|
"reset_achievements_error": "Başarımlar sıfırlanamadı",
|
||||||
|
"download_error_gofile_quota_exceeded": "Gofile aylık kotanızı doldurdunuz. Kotanın yenilenmesini bekleyin.",
|
||||||
|
"download_error_real_debrid_account_not_authorized": "Real-Debrid hesabınız yeni indirme işlemleri yapmak için yetkilendirilmemiş. Lütfen hesap ayarlarınızı kontrol edip tekrar deneyin.",
|
||||||
|
"download_error_not_cached_in_real_debrid": "Bu indirme Real-Debrid üzerinde mevcut değil ve Real-Debrid'den indirme durumu henüz sorgulanamıyor.",
|
||||||
|
"download_error_not_cached_in_torbox": "Bu indirme Torbox'ta mevcut değil ve Torbox'tan indirme durumu henüz sorgulanamıyor.",
|
||||||
|
"game_removed_from_favorites": "Oyun favorilerden silindi",
|
||||||
|
"game_added_to_favorites": "Oyun favorilere eklendi"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Hydra'yı Aktive Et",
|
"title": "Hydra'yı Aktive Et",
|
||||||
"installation_id": "Kurulum Kimliği:",
|
"installation_id": "Kurulum Kimliği:",
|
||||||
"enter_activation_code": "Aktivasyon kodunuzu girin",
|
"enter_activation_code": "Aktivasyon kodunuzu girin",
|
||||||
"message": "Bunu nereden soracağınızı bilmiyorsanız, bu sizin için olmamalı.",
|
"message": "Bunu nasıl edineceğini bilmiyorsan, buna sahip olmamalısın.",
|
||||||
"activate": "Aktive Et",
|
"activate": "Aktive Et",
|
||||||
"loading": "Yükleniyor…"
|
"loading": "Yükleniyor…"
|
||||||
},
|
},
|
||||||
@@ -206,7 +214,7 @@
|
|||||||
"cancel": "İptal Et",
|
"cancel": "İptal Et",
|
||||||
"filter": "İndirilen oyunları filtrele",
|
"filter": "İndirilen oyunları filtrele",
|
||||||
"remove": "Kaldır",
|
"remove": "Kaldır",
|
||||||
"downloading_metadata": "Metadata indiriliyor…",
|
"downloading_metadata": "Meta verileri indiriliyor…",
|
||||||
"deleting": "Yükleyici siliniyor…",
|
"deleting": "Yükleyici siliniyor…",
|
||||||
"delete": "Yükleyiciyi kaldır",
|
"delete": "Yükleyiciyi kaldır",
|
||||||
"delete_modal_title": "Emin misiniz?",
|
"delete_modal_title": "Emin misiniz?",
|
||||||
@@ -231,7 +239,7 @@
|
|||||||
"enable_download_notifications": "Bir indirme tamamlandığında",
|
"enable_download_notifications": "Bir indirme tamamlandığında",
|
||||||
"enable_repack_list_notifications": "Yeni bir repack eklendiğinde",
|
"enable_repack_list_notifications": "Yeni bir repack eklendiğinde",
|
||||||
"real_debrid_api_token_label": "Real-Debrid API anahtarı",
|
"real_debrid_api_token_label": "Real-Debrid API anahtarı",
|
||||||
"quit_app_instead_hiding": "Hydra'yı kapatırken gizlemeyin",
|
"quit_app_instead_hiding": "Hydra'yı kapatınca sistem tepsisine gitmesin",
|
||||||
"launch_with_system": "Hydra'yı sistem başlatıldığında çalıştır",
|
"launch_with_system": "Hydra'yı sistem başlatıldığında çalıştır",
|
||||||
"general": "Genel",
|
"general": "Genel",
|
||||||
"behavior": "Davranış",
|
"behavior": "Davranış",
|
||||||
@@ -250,6 +258,12 @@
|
|||||||
"validate_download_source": "Doğrula",
|
"validate_download_source": "Doğrula",
|
||||||
"remove_download_source": "Kaldır",
|
"remove_download_source": "Kaldır",
|
||||||
"add_download_source": "Kaynak ekle",
|
"add_download_source": "Kaynak ekle",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "Hayır",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Evet, her şeyi sil",
|
||||||
|
"description_confirmation_delete_all_sources": "Tüm indirme kaynaklarını sileceksiniz",
|
||||||
|
"title_confirmation_delete_all_sources": "Tüm indirme kaynaklarını sil",
|
||||||
|
"removed_download_sources": "Yazı tipleri kaldırıldı",
|
||||||
|
"button_delete_all_sources": "Tüm indirme kaynaklarını kaldır",
|
||||||
"download_count_zero": "İndirme seçeneği yok",
|
"download_count_zero": "İndirme seçeneği yok",
|
||||||
"download_count_one": "{{countFormatted}} indirme seçeneği",
|
"download_count_one": "{{countFormatted}} indirme seçeneği",
|
||||||
"download_count_other": "{{countFormatted}} indirme seçeneği",
|
"download_count_other": "{{countFormatted}} indirme seçeneği",
|
||||||
@@ -277,11 +291,56 @@
|
|||||||
"must_be_valid_url": "Kaynak geçerli bir URL olmalıdır",
|
"must_be_valid_url": "Kaynak geçerli bir URL olmalıdır",
|
||||||
"blocked_users": "Engellenen kullanıcılar",
|
"blocked_users": "Engellenen kullanıcılar",
|
||||||
"user_unblocked": "Kullanıcının engeli kaldırıldı",
|
"user_unblocked": "Kullanıcının engeli kaldırıldı",
|
||||||
"enable_achievement_notifications": "Bir başarı kilidi açıldığında",
|
"enable_achievement_notifications": "Bir başarım kilidi açıldığında",
|
||||||
"launch_minimized": "Hydra'yı küçültülmüş başlat",
|
"launch_minimized": "Hydra'yı küçültülmüş başlat",
|
||||||
"disable_nsfw_alert": "NSFW uyarısını devre dışı bırak",
|
"disable_nsfw_alert": "NSFW uyarısını devre dışı bırak",
|
||||||
"seed_after_download_complete": "İndirme tamamlandıktan sonra paylaş",
|
"seed_after_download_complete": "İndirme tamamlandıktan sonra paylaş",
|
||||||
"show_hidden_achievement_description": "Gizli başarı açıklamalarını kilitlenmeden önce göster"
|
"show_hidden_achievement_description": "Gizli başarım açıklamalarını kilitlenmeden önce göster",
|
||||||
|
"account": "Hesap",
|
||||||
|
"no_users_blocked": "Hiçbir kullanıcıyı engellemediniz",
|
||||||
|
"subscription_active_until": "Hydra Cloud'unuz {{date}} tarihine kadar aktif",
|
||||||
|
"manage_subscription": "Aboneliği yönet",
|
||||||
|
"update_email": "E-posta'yı güncelle",
|
||||||
|
"update_password": "Şifreyi güncelle",
|
||||||
|
"current_email": "Aktif e-posta'nız",
|
||||||
|
"no_email_account": "Henüz ayarlanmış bir e-postanız yok",
|
||||||
|
"account_data_updated_successfully": "Hesap bilgileri başarıyla güncellendi",
|
||||||
|
"renew_subscription": "Hydra Cloud'u yenile",
|
||||||
|
"subscription_expired_at": "Aboneliğiniz {{date}} tarihinde sona erdi",
|
||||||
|
"no_subscription": "Hydra'yı en iyi şekilde deneyimleyin",
|
||||||
|
"become_subscriber": "Hydra Cloud'lu ol",
|
||||||
|
"subscription_renew_cancelled": "Otomatik yenileme devre dışı",
|
||||||
|
"subscription_renews_on": "Aboneliğiniz {{date}} tarihinde yenilenecek",
|
||||||
|
"bill_sent_until": "Bir sonraki faturanız bu tarihe kadar gönderilecek",
|
||||||
|
"no_themes": "Henüz bir temanız yok gibi görünüyor, ama endişelenmeyin, ilk şaheserinizi oluşturmak için buraya tıklayın.",
|
||||||
|
"editor_tab_code": "Kod",
|
||||||
|
"editor_tab_info": "Bilgi",
|
||||||
|
"editor_tab_save": "Kaydet",
|
||||||
|
"web_store": "İnternet mağazası",
|
||||||
|
"clear_themes": "Temizle",
|
||||||
|
"create_theme": "Oluştur",
|
||||||
|
"create_theme_modal_title": "Tema oluştur",
|
||||||
|
"create_theme_modal_description": "Hydra'nın görünümünü özelleştirmek için yeni bir tema oluştur",
|
||||||
|
"theme_name": "İsim",
|
||||||
|
"insert_theme_name": "Tema ismini gir",
|
||||||
|
"set_theme": "Temayı seç",
|
||||||
|
"unset_theme": "Tema seçimini kaldır",
|
||||||
|
"delete_theme": "Temayı sil",
|
||||||
|
"edit_theme": "Temayı düzenle",
|
||||||
|
"delete_all_themes": "Tüm temaları sil",
|
||||||
|
"delete_all_themes_description": "Bu tüm temalarınızı silecektir",
|
||||||
|
"delete_theme_description": "Bu {{theme}} temasını silecektir",
|
||||||
|
"cancel": "İptal",
|
||||||
|
"appearance": "Görünüm",
|
||||||
|
"enable_torbox": "Torbox'u etkinleştir",
|
||||||
|
"torbox_description": "TorBox, piyasadaki en iyi sunucularla bile rekabet edebilen premium seedbox hizmetinizdir.",
|
||||||
|
"torbox_account_linked": "TorBox hesabı bağlando",
|
||||||
|
"real_debrid_account_linked": "Real-Debrid hesabı bağlando",
|
||||||
|
"name_min_length": "Tema ismi en az 3 karakter uzunluğunda olmalıdır",
|
||||||
|
"import_theme": "Temayı içe aktar",
|
||||||
|
"import_theme_description": "{{theme}} teması, tema mağazasından içeri aktarılacak",
|
||||||
|
"error_importing_theme": "Temayı içe aktarmada bir sorun oluştu",
|
||||||
|
"theme_imported": "Tema başarıyla içe aktarıldı"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "İndirme tamamlandı",
|
"download_complete": "İndirme tamamlandı",
|
||||||
@@ -289,17 +348,19 @@
|
|||||||
"repack_list_updated": "Repack listesi güncellendi",
|
"repack_list_updated": "Repack listesi güncellendi",
|
||||||
"repack_count_one": "{{count}} repack eklendi",
|
"repack_count_one": "{{count}} repack eklendi",
|
||||||
"repack_count_other": "{{count}} repack eklendi",
|
"repack_count_other": "{{count}} repack eklendi",
|
||||||
"new_update_available": "Sürüm {{version}} mevcut",
|
"new_update_available": "{{version}} sürümü mevcut",
|
||||||
"restart_to_install_update": "Güncellemeyi yüklemek için Hydra'yı yeniden başlatın",
|
"restart_to_install_update": "Güncellemeyi yüklemek için Hydra'yı yeniden başlatın",
|
||||||
"notification_achievement_unlocked_title": "{{game}} için başarı kilidi açıldı",
|
"notification_achievement_unlocked_title": "{{game}} için başarım kilidi açıldı",
|
||||||
"notification_achievement_unlocked_body": "{{achievement}} ve diğer {{count}} başarılar açıldı"
|
"notification_achievement_unlocked_body": "{{achievement}} ve diğer {{count}} başarım açıldı"
|
||||||
},
|
},
|
||||||
"system_tray": {
|
"system_tray": {
|
||||||
"open": "Hydra'yı Aç",
|
"open": "Hydra'yı Aç",
|
||||||
"quit": "Çık"
|
"quit": "Çık"
|
||||||
},
|
},
|
||||||
"game_card": {
|
"game_card": {
|
||||||
"no_downloads": "İndirilebilir içerik bulunmuyor"
|
"no_downloads": "İndirilebilir içerik bulunmuyor",
|
||||||
|
"available_one": "Mevcut",
|
||||||
|
"available_other": "Mevcut"
|
||||||
},
|
},
|
||||||
"binary_not_found_modal": {
|
"binary_not_found_modal": {
|
||||||
"title": "Programlar Yüklü Değil",
|
"title": "Programlar Yüklü Değil",
|
||||||
@@ -332,7 +393,7 @@
|
|||||||
"successfully_signed_out": "Başarıyla çıkış yapıldı",
|
"successfully_signed_out": "Başarıyla çıkış yapıldı",
|
||||||
"sign_out": "Çıkış yap",
|
"sign_out": "Çıkış yap",
|
||||||
"playing_for": "{{amount}} oynanıyor",
|
"playing_for": "{{amount}} oynanıyor",
|
||||||
"sign_out_modal_text": "Kütüphaneniz mevcut hesabınıza bağlı. Çıkış yaptığınızda kütüphaneniz görünür olmayacak ve herhangi bir ilerleme kaydedilmeyecek. Çıkışa devam etmek istiyor musunuz?",
|
"sign_out_modal_text": "Kütüphaneniz mevcut hesabınıza bağlı. Oturumu kapattığınızda kütüphaneniz görünür olmayacak ve herhangi bir ilerleme kaydedilmeyecek. Oturumu kapatmaya devam etmek istiyor musunuz?",
|
||||||
"add_friends": "Arkadaş Ekle",
|
"add_friends": "Arkadaş Ekle",
|
||||||
"add": "Ekle",
|
"add": "Ekle",
|
||||||
"friend_code": "Arkadaş kodu",
|
"friend_code": "Arkadaş kodu",
|
||||||
@@ -383,39 +444,39 @@
|
|||||||
"uploading_banner": "Afiş yükleniyor…",
|
"uploading_banner": "Afiş yükleniyor…",
|
||||||
"background_image_updated": "Arka plan görüntüsü güncellendi",
|
"background_image_updated": "Arka plan görüntüsü güncellendi",
|
||||||
"stats": "İstatistikler",
|
"stats": "İstatistikler",
|
||||||
"achievements": "Başarılar",
|
"achievements": "Başarımlar",
|
||||||
"games": "Oyunlar",
|
"games": "Oyunlar",
|
||||||
"top_percentile": "En üst {{percentile}}%",
|
"top_percentile": "En üst {{percentile}}%",
|
||||||
"ranking_updated_weekly": "Sıralama haftalık olarak güncellenir",
|
"ranking_updated_weekly": "Sıralama haftalık olarak güncellenir",
|
||||||
"playing": "{{game}} oynanıyor",
|
"playing": "{{game}} oynanıyor",
|
||||||
"achievements_unlocked": "Başarılar açıldı",
|
"achievements_unlocked": "Başarımlar açıldı",
|
||||||
"earned_points": "Kazanılan puanlar",
|
"earned_points": "Kazanılan puanlar",
|
||||||
"show_achievements_on_profile": "Başarılarınızı profilinizde gösterin",
|
"show_achievements_on_profile": "Başarımlarınızı profilinizde gösterin",
|
||||||
"show_points_on_profile": "Kazandığınız puanları profilinizde gösterin"
|
"show_points_on_profile": "Kazandığınız puanları profilinizde gösterin"
|
||||||
},
|
},
|
||||||
"achievement": {
|
"achievement": {
|
||||||
"achievement_unlocked": "Başarı açıldı",
|
"achievement_unlocked": "Başarım açıldı",
|
||||||
"user_achievements": "{{displayName}}'in Başarıları",
|
"user_achievements": "{{displayName}} oyununun Başarımları",
|
||||||
"your_achievements": "Başarılarınız",
|
"your_achievements": "Başarımlarınız",
|
||||||
"unlocked_at": "Açılma zamanı: {{date}}",
|
"unlocked_at": "Açılma zamanı: {{date}}",
|
||||||
"subscription_needed": "Bu içeriği görmek için bir Hydra Cloud aboneliği gereklidir",
|
"subscription_needed": "Bu içeriği görmek için bir Hydra Cloud aboneliği gereklidir",
|
||||||
"new_achievements_unlocked": "{{gameCount}} oyundan {{achievementCount}} yeni başarı açıldı",
|
"new_achievements_unlocked": "{{gameCount}} oyundan {{achievementCount}} yeni başarım açıldı",
|
||||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} başarı",
|
"achievement_progress": "{{unlockedCount}}/{{totalCount}} başarım",
|
||||||
"achievements_unlocked_for_game": "{{gameTitle}} oyunu için {{achievementCount}} yeni başarı açıldı",
|
"achievements_unlocked_for_game": "{{gameTitle}} oyunu için {{achievementCount}} yeni başarım açıldı",
|
||||||
"hidden_achievement_tooltip": "Bu gizli bir başarıdır",
|
"hidden_achievement_tooltip": "Bu gizli bir başarımdır",
|
||||||
"achievement_earn_points": "Bu başarı ile {{points}} puan kazanın",
|
"achievement_earn_points": "Bu başarım ile {{points}} puan kazanın",
|
||||||
"earned_points": "Kazanılan puanlar:",
|
"earned_points": "Kazanılan puanlar:",
|
||||||
"available_points": "Mevcut puanlar:",
|
"available_points": "Mevcut puanlar:",
|
||||||
"how_to_earn_achievements_points": "Başarı puanları nasıl kazanılır?"
|
"how_to_earn_achievements_points": "Başarım puanları nasıl kazanılır?"
|
||||||
},
|
},
|
||||||
"hydra_cloud": {
|
"hydra_cloud": {
|
||||||
"subscription_tour_title": "Hydra Cloud Aboneliği",
|
"subscription_tour_title": "Hydra Cloud Aboneliği",
|
||||||
"subscribe_now": "Şimdi abone olun",
|
"subscribe_now": "Şimdi abone olun",
|
||||||
"cloud_saving": "Bulut kaydetme",
|
"cloud_saving": "Bulut kaydetme",
|
||||||
"cloud_achievements": "Başarılarınızı buluta kaydedin",
|
"cloud_achievements": "Başarımlarınızı buluta kaydedin",
|
||||||
"animated_profile_picture": "Animasyonlu profil resimleri",
|
"animated_profile_picture": "Animasyonlu profil resimleri",
|
||||||
"premium_support": "Premium Destek",
|
"premium_support": "Premium Destek",
|
||||||
"show_and_compare_achievements": "Başarılarınızı diğer kullanıcılarla karşılaştırın ve gösterin",
|
"show_and_compare_achievements": "Başarımlarınızı diğer kullanıcılarla karşılaştırın ve gösterin",
|
||||||
"animated_profile_banner": "Animasyonlu profil afişi",
|
"animated_profile_banner": "Animasyonlu profil afişi",
|
||||||
"hydra_cloud": "Hydra Cloud",
|
"hydra_cloud": "Hydra Cloud",
|
||||||
"hydra_cloud_feature_found": "Bir Hydra Cloud özelliği keşfettiniz!",
|
"hydra_cloud_feature_found": "Bir Hydra Cloud özelliği keşfettiniz!",
|
||||||
|
|||||||
@@ -176,6 +176,12 @@
|
|||||||
"import": "Імпортувати",
|
"import": "Імпортувати",
|
||||||
"insert_valid_json_url": "Вставте дійсний URL JSON-файлу",
|
"insert_valid_json_url": "Вставте дійсний URL JSON-файлу",
|
||||||
"language": "Мова",
|
"language": "Мова",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "немає",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "Так, видалити все",
|
||||||
|
"description_confirmation_delete_all_sources": "Ви видалите всі джерела завантаження",
|
||||||
|
"title_confirmation_delete_all_sources": "Видалити всі джерела завантаження",
|
||||||
|
"removed_download_sources": "Джерела завантажень видалено",
|
||||||
|
"button_delete_all_sources": "Видаліть усі джерела завантаження",
|
||||||
"api_token": "API-токен",
|
"api_token": "API-токен",
|
||||||
"debrid_api_token_hint": "API токен можливо отримати <0>тут</0>",
|
"debrid_api_token_hint": "API токен можливо отримати <0>тут</0>",
|
||||||
"real_debrid_api_token_label": "Real-Debrid API-токен",
|
"real_debrid_api_token_label": "Real-Debrid API-токен",
|
||||||
|
|||||||
@@ -226,6 +226,12 @@
|
|||||||
"download_sources_description": "Hydra 将从这些源获取下载链接。源 URL 必须是直接链接到包含下载链接的 .json 文件。",
|
"download_sources_description": "Hydra 将从这些源获取下载链接。源 URL 必须是直接链接到包含下载链接的 .json 文件。",
|
||||||
"validate_download_source": "验证",
|
"validate_download_source": "验证",
|
||||||
"remove_download_source": "移除",
|
"remove_download_source": "移除",
|
||||||
|
"cancel_button_confirmation_delete_all_sources": "不",
|
||||||
|
"confirm_button_confirmation_delete_all_sources": "是的,删除所有内容",
|
||||||
|
"description_confirmation_delete_all_sources": "您将删除所有下载源",
|
||||||
|
"title_confirmation_delete_all_sources": "删除所有下载源",
|
||||||
|
"removed_download_sources": "已删除字体",
|
||||||
|
"button_delete_all_sources": "删除所有下载源",
|
||||||
"add_download_source": "添加源",
|
"add_download_source": "添加源",
|
||||||
"download_count_zero": "列表中无下载",
|
"download_count_zero": "列表中无下载",
|
||||||
"download_count_one": "列表中有 {{countFormatted}} 个下载",
|
"download_count_one": "列表中有 {{countFormatted}} 个下载",
|
||||||
|
|||||||
@@ -31,3 +31,5 @@ export const achievementSoundPath = app.isPackaged
|
|||||||
export const backupsPath = path.join(app.getPath("userData"), "Backups");
|
export const backupsPath = path.join(app.getPath("userData"), "Backups");
|
||||||
|
|
||||||
export const appVersion = app.getVersion() + (isStaging ? "-staging" : "");
|
export const appVersion = app.getVersion() + (isStaging ? "-staging" : "");
|
||||||
|
|
||||||
|
export const MAIN_LOOP_INTERVAL = 1500;
|
||||||
|
|||||||
@@ -4,11 +4,15 @@ import updater from "electron-updater";
|
|||||||
|
|
||||||
const { autoUpdater } = updater;
|
const { autoUpdater } = updater;
|
||||||
|
|
||||||
const restartAndInstallUpdate = async (_event: Electron.IpcMainInvokeEvent) => {
|
export const restartAndInstallUpdate = () => {
|
||||||
autoUpdater.removeAllListeners();
|
autoUpdater.removeAllListeners();
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
autoUpdater.quitAndInstall(false);
|
autoUpdater.quitAndInstall(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
registerEvent("restartAndInstallUpdate", restartAndInstallUpdate);
|
const restartAndInstallUpdateEvent = async (
|
||||||
|
_event: Electron.IpcMainInvokeEvent
|
||||||
|
) => restartAndInstallUpdate();
|
||||||
|
|
||||||
|
registerEvent("restartAndInstallUpdate", restartAndInstallUpdateEvent);
|
||||||
|
|||||||
@@ -1,44 +1,8 @@
|
|||||||
import { HydraApi, logger, Ludusavi, WindowManager } from "@main/services";
|
import { CloudSync } from "@main/services";
|
||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import fs from "node:fs";
|
|
||||||
import path from "node:path";
|
|
||||||
import * as tar from "tar";
|
|
||||||
import crypto from "node:crypto";
|
|
||||||
import type { GameShop } from "@types";
|
import type { GameShop } from "@types";
|
||||||
import axios from "axios";
|
import { t } from "i18next";
|
||||||
import os from "node:os";
|
import { format } from "date-fns";
|
||||||
import { backupsPath } from "@main/constants";
|
|
||||||
import { app } from "electron";
|
|
||||||
import { normalizePath } from "@main/helpers";
|
|
||||||
import { gamesSublevel, levelKeys } from "@main/level";
|
|
||||||
|
|
||||||
const bundleBackup = async (
|
|
||||||
shop: GameShop,
|
|
||||||
objectId: string,
|
|
||||||
winePrefix: string | null
|
|
||||||
) => {
|
|
||||||
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
|
|
||||||
|
|
||||||
// Remove existing backup
|
|
||||||
if (fs.existsSync(backupPath)) {
|
|
||||||
fs.rmSync(backupPath, { recursive: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
await Ludusavi.backupGame(shop, objectId, backupPath, winePrefix);
|
|
||||||
|
|
||||||
const tarLocation = path.join(backupsPath, `${crypto.randomUUID()}.tar`);
|
|
||||||
|
|
||||||
await tar.create(
|
|
||||||
{
|
|
||||||
gzip: false,
|
|
||||||
file: tarLocation,
|
|
||||||
cwd: backupPath,
|
|
||||||
},
|
|
||||||
["."]
|
|
||||||
);
|
|
||||||
|
|
||||||
return tarLocation;
|
|
||||||
};
|
|
||||||
|
|
||||||
const uploadSaveGame = async (
|
const uploadSaveGame = async (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
@@ -46,61 +10,15 @@ const uploadSaveGame = async (
|
|||||||
shop: GameShop,
|
shop: GameShop,
|
||||||
downloadOptionTitle: string | null
|
downloadOptionTitle: string | null
|
||||||
) => {
|
) => {
|
||||||
const game = await gamesSublevel.get(levelKeys.game(shop, objectId));
|
return CloudSync.uploadSaveGame(
|
||||||
|
|
||||||
const bundleLocation = await bundleBackup(
|
|
||||||
shop,
|
|
||||||
objectId,
|
objectId,
|
||||||
game?.winePrefixPath ?? null
|
shop,
|
||||||
|
downloadOptionTitle,
|
||||||
|
t("backup_from", {
|
||||||
|
ns: "game_details",
|
||||||
|
date: format(new Date(), "dd/MM/yyyy"),
|
||||||
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
fs.stat(bundleLocation, async (err, stat) => {
|
|
||||||
if (err) {
|
|
||||||
logger.error("Failed to get zip file stats", err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { uploadUrl } = await HydraApi.post<{
|
|
||||||
id: string;
|
|
||||||
uploadUrl: string;
|
|
||||||
}>("/profile/games/artifacts", {
|
|
||||||
artifactLengthInBytes: stat.size,
|
|
||||||
shop,
|
|
||||||
objectId,
|
|
||||||
hostname: os.hostname(),
|
|
||||||
homeDir: normalizePath(app.getPath("home")),
|
|
||||||
downloadOptionTitle,
|
|
||||||
platform: os.platform(),
|
|
||||||
});
|
|
||||||
|
|
||||||
fs.readFile(bundleLocation, async (err, fileBuffer) => {
|
|
||||||
if (err) {
|
|
||||||
logger.error("Failed to read zip file", err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
await axios.put(uploadUrl, fileBuffer, {
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/tar",
|
|
||||||
},
|
|
||||||
onUploadProgress: (progressEvent) => {
|
|
||||||
logger.log(progressEvent);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
WindowManager.mainWindow?.webContents.send(
|
|
||||||
`on-upload-complete-${objectId}-${shop}`,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
fs.rm(bundleLocation, (err) => {
|
|
||||||
if (err) {
|
|
||||||
logger.error("Failed to remove tar file", err);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
registerEvent("uploadSaveGame", uploadSaveGame);
|
registerEvent("uploadSaveGame", uploadSaveGame);
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ import "./library/remove-game";
|
|||||||
import "./library/remove-game-from-library";
|
import "./library/remove-game-from-library";
|
||||||
import "./library/select-game-wine-prefix";
|
import "./library/select-game-wine-prefix";
|
||||||
import "./library/reset-game-achievements";
|
import "./library/reset-game-achievements";
|
||||||
|
import "./library/toggle-automatic-cloud-sync";
|
||||||
import "./misc/open-checkout";
|
import "./misc/open-checkout";
|
||||||
import "./misc/open-external";
|
import "./misc/open-external";
|
||||||
import "./misc/show-open-dialog";
|
import "./misc/show-open-dialog";
|
||||||
import "./misc/get-features";
|
import "./misc/get-features";
|
||||||
import "./misc/show-item-in-folder";
|
import "./misc/show-item-in-folder";
|
||||||
|
import "./misc/get-badges";
|
||||||
import "./torrenting/cancel-game-download";
|
import "./torrenting/cancel-game-download";
|
||||||
import "./torrenting/pause-game-download";
|
import "./torrenting/pause-game-download";
|
||||||
import "./torrenting/resume-game-download";
|
import "./torrenting/resume-game-download";
|
||||||
@@ -58,6 +60,7 @@ import "./user/get-blocked-users";
|
|||||||
import "./user/block-user";
|
import "./user/block-user";
|
||||||
import "./user/unblock-user";
|
import "./user/unblock-user";
|
||||||
import "./user/get-user-friends";
|
import "./user/get-user-friends";
|
||||||
|
import "./user/get-auth";
|
||||||
import "./user/get-user-stats";
|
import "./user/get-user-stats";
|
||||||
import "./user/report-user";
|
import "./user/report-user";
|
||||||
import "./user/get-unlocked-achievements";
|
import "./user/get-unlocked-achievements";
|
||||||
|
|||||||
23
src/main/events/library/toggle-automatic-cloud-sync.ts
Normal file
23
src/main/events/library/toggle-automatic-cloud-sync.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { registerEvent } from "../register-event";
|
||||||
|
import { levelKeys, gamesSublevel } from "@main/level";
|
||||||
|
import type { GameShop } from "@types";
|
||||||
|
|
||||||
|
const toggleAutomaticCloudSync = async (
|
||||||
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
|
shop: GameShop,
|
||||||
|
objectId: string,
|
||||||
|
automaticCloudSync: boolean
|
||||||
|
) => {
|
||||||
|
const gameKey = levelKeys.game(shop, objectId);
|
||||||
|
|
||||||
|
const game = await gamesSublevel.get(gameKey);
|
||||||
|
|
||||||
|
if (!game) return;
|
||||||
|
|
||||||
|
await gamesSublevel.put(gameKey, {
|
||||||
|
...game,
|
||||||
|
automaticCloudSync,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEvent("toggleAutomaticCloudSync", toggleAutomaticCloudSync);
|
||||||
22
src/main/events/misc/get-badges.ts
Normal file
22
src/main/events/misc/get-badges.ts
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import { Badge } from "@types";
|
||||||
|
import { registerEvent } from "../register-event";
|
||||||
|
import { HydraApi } from "@main/services";
|
||||||
|
import { db, levelKeys } from "@main/level";
|
||||||
|
|
||||||
|
const getBadges = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||||
|
const language = await db
|
||||||
|
.get<string, string>(levelKeys.language, {
|
||||||
|
valueEncoding: "utf-8",
|
||||||
|
})
|
||||||
|
.then((language) => language || "en");
|
||||||
|
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
locale: language,
|
||||||
|
});
|
||||||
|
|
||||||
|
return HydraApi.get<Badge[]>(`/badges?${params.toString()}`, null, {
|
||||||
|
needsAuth: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEvent("getBadges", getBadges);
|
||||||
@@ -1,17 +1,59 @@
|
|||||||
|
import { MAIN_LOOP_INTERVAL } from "@main/constants";
|
||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import { HydraApi } from "@main/services";
|
import { HydraApi, WindowManager } from "@main/services";
|
||||||
|
import { publishNewFriendRequestNotification } from "@main/services/notifications";
|
||||||
import { UserNotLoggedInError } from "@shared";
|
import { UserNotLoggedInError } from "@shared";
|
||||||
import type { FriendRequestSync } from "@types";
|
import type { FriendRequestSync } from "@types";
|
||||||
|
|
||||||
const syncFriendRequests = async (_event: Electron.IpcMainInvokeEvent) => {
|
interface SyncState {
|
||||||
return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`).catch(
|
friendRequestCount: number | null;
|
||||||
(err) => {
|
tick: number;
|
||||||
if (err instanceof UserNotLoggedInError) {
|
}
|
||||||
return { friendRequests: [] };
|
|
||||||
}
|
const ticksToUpdate = (2 * 60 * 1000) / MAIN_LOOP_INTERVAL; // 2 minutes
|
||||||
throw err;
|
|
||||||
}
|
const syncState: SyncState = {
|
||||||
);
|
friendRequestCount: null,
|
||||||
|
tick: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
registerEvent("syncFriendRequests", syncFriendRequests);
|
const syncFriendRequests = async () => {
|
||||||
|
return HydraApi.get<FriendRequestSync>(`/profile/friend-requests/sync`)
|
||||||
|
.then((res) => {
|
||||||
|
if (
|
||||||
|
syncState.friendRequestCount != null &&
|
||||||
|
syncState.friendRequestCount < res.friendRequestCount
|
||||||
|
) {
|
||||||
|
publishNewFriendRequestNotification();
|
||||||
|
}
|
||||||
|
|
||||||
|
syncState.friendRequestCount = res.friendRequestCount;
|
||||||
|
|
||||||
|
WindowManager.mainWindow?.webContents.send(
|
||||||
|
"on-sync-friend-requests",
|
||||||
|
res
|
||||||
|
);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
if (err instanceof UserNotLoggedInError) {
|
||||||
|
return { friendRequestCount: 0 } as FriendRequestSync;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const syncFriendRequestsEvent = async (_event: Electron.IpcMainInvokeEvent) => {
|
||||||
|
return syncFriendRequests();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const watchFriendRequests = async () => {
|
||||||
|
if (syncState.tick % ticksToUpdate === 0) {
|
||||||
|
await syncFriendRequests();
|
||||||
|
}
|
||||||
|
|
||||||
|
syncState.tick++;
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEvent("syncFriendRequests", syncFriendRequestsEvent);
|
||||||
|
|||||||
11
src/main/events/user/get-auth.ts
Normal file
11
src/main/events/user/get-auth.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { db, levelKeys } from "@main/level";
|
||||||
|
import type { Auth } from "@types";
|
||||||
|
|
||||||
|
import { registerEvent } from "../register-event";
|
||||||
|
|
||||||
|
const getAuth = async (_event: Electron.IpcMainInvokeEvent) =>
|
||||||
|
db.get<string, Auth>(levelKeys.auth, {
|
||||||
|
valueEncoding: "json",
|
||||||
|
});
|
||||||
|
|
||||||
|
registerEvent("getAuth", getAuth);
|
||||||
@@ -14,4 +14,5 @@ export const levelKeys = {
|
|||||||
userPreferences: "userPreferences",
|
userPreferences: "userPreferences",
|
||||||
language: "language",
|
language: "language",
|
||||||
sqliteMigrationDone: "sqliteMigrationDone",
|
sqliteMigrationDone: "sqliteMigrationDone",
|
||||||
|
screenState: "screenState",
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -62,8 +62,6 @@ export const loadState = async () => {
|
|||||||
game.uri !== null
|
game.uri !== null
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("downloadsToSeed", downloadsToSeed);
|
|
||||||
|
|
||||||
await DownloadManager.startRPC(nextItemOnQueue, downloadsToSeed);
|
await DownloadManager.startRPC(nextItemOnQueue, downloadsToSeed);
|
||||||
|
|
||||||
startMainLoop();
|
startMainLoop();
|
||||||
@@ -132,7 +130,9 @@ const migrateFromSqlite = async () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (rest.language) {
|
if (rest.language) {
|
||||||
await db.put(levelKeys.language, rest.language);
|
await db.put<string, string>(levelKeys.language, rest.language, {
|
||||||
|
valueEncoding: "utf-8",
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
111
src/main/services/cloud-sync.ts
Normal file
111
src/main/services/cloud-sync.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
import { levelKeys, gamesSublevel, db } from "@main/level";
|
||||||
|
import { app } from "electron";
|
||||||
|
import path from "node:path";
|
||||||
|
import * as tar from "tar";
|
||||||
|
import crypto from "node:crypto";
|
||||||
|
import fs from "node:fs";
|
||||||
|
import os from "node:os";
|
||||||
|
import type { GameShop, User } from "@types";
|
||||||
|
import { backupsPath } from "@main/constants";
|
||||||
|
import { HydraApi } from "./hydra-api";
|
||||||
|
import { normalizePath } from "@main/helpers";
|
||||||
|
import { logger } from "./logger";
|
||||||
|
import { WindowManager } from "./window-manager";
|
||||||
|
import axios from "axios";
|
||||||
|
import { Ludusavi } from "./ludusavi";
|
||||||
|
import { SubscriptionRequiredError } from "@shared";
|
||||||
|
|
||||||
|
export class CloudSync {
|
||||||
|
private static async bundleBackup(
|
||||||
|
shop: GameShop,
|
||||||
|
objectId: string,
|
||||||
|
winePrefix: string | null
|
||||||
|
) {
|
||||||
|
const backupPath = path.join(backupsPath, `${shop}-${objectId}`);
|
||||||
|
|
||||||
|
// Remove existing backup
|
||||||
|
if (fs.existsSync(backupPath)) {
|
||||||
|
fs.rmSync(backupPath, { recursive: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
await Ludusavi.backupGame(shop, objectId, backupPath, winePrefix);
|
||||||
|
|
||||||
|
const tarLocation = path.join(backupsPath, `${crypto.randomUUID()}.tar`);
|
||||||
|
|
||||||
|
await tar.create(
|
||||||
|
{
|
||||||
|
gzip: false,
|
||||||
|
file: tarLocation,
|
||||||
|
cwd: backupPath,
|
||||||
|
},
|
||||||
|
["."]
|
||||||
|
);
|
||||||
|
|
||||||
|
return tarLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async uploadSaveGame(
|
||||||
|
objectId: string,
|
||||||
|
shop: GameShop,
|
||||||
|
downloadOptionTitle: string | null,
|
||||||
|
label?: string
|
||||||
|
) {
|
||||||
|
const hasActiveSubscription = await db
|
||||||
|
.get<string, User>(levelKeys.user, { valueEncoding: "json" })
|
||||||
|
.then((user) => {
|
||||||
|
const expiresAt = new Date(user?.subscription?.expiresAt ?? 0);
|
||||||
|
return expiresAt > new Date();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!hasActiveSubscription) {
|
||||||
|
throw new SubscriptionRequiredError();
|
||||||
|
}
|
||||||
|
|
||||||
|
const game = await gamesSublevel.get(levelKeys.game(shop, objectId));
|
||||||
|
|
||||||
|
const bundleLocation = await this.bundleBackup(
|
||||||
|
shop,
|
||||||
|
objectId,
|
||||||
|
game?.winePrefixPath ?? null
|
||||||
|
);
|
||||||
|
|
||||||
|
const stat = await fs.promises.stat(bundleLocation);
|
||||||
|
|
||||||
|
const { uploadUrl } = await HydraApi.post<{
|
||||||
|
id: string;
|
||||||
|
uploadUrl: string;
|
||||||
|
}>("/profile/games/artifacts", {
|
||||||
|
artifactLengthInBytes: stat.size,
|
||||||
|
shop,
|
||||||
|
objectId,
|
||||||
|
hostname: os.hostname(),
|
||||||
|
homeDir: normalizePath(app.getPath("home")),
|
||||||
|
downloadOptionTitle,
|
||||||
|
platform: os.platform(),
|
||||||
|
label,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fileBuffer = await fs.promises.readFile(bundleLocation);
|
||||||
|
|
||||||
|
await axios.put(uploadUrl, fileBuffer, {
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/tar",
|
||||||
|
},
|
||||||
|
onUploadProgress: (progressEvent) => {
|
||||||
|
logger.log(progressEvent);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
WindowManager.mainWindow?.webContents.send(
|
||||||
|
`on-upload-complete-${objectId}-${shop}`,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
fs.rm(bundleLocation, (err) => {
|
||||||
|
if (err) {
|
||||||
|
logger.error("Failed to remove tar file", err);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,13 @@ import { Downloader, DownloadError } from "@shared";
|
|||||||
import { WindowManager } from "../window-manager";
|
import { WindowManager } from "../window-manager";
|
||||||
import { publishDownloadCompleteNotification } from "../notifications";
|
import { publishDownloadCompleteNotification } from "../notifications";
|
||||||
import type { Download, DownloadProgress, UserPreferences } from "@types";
|
import type { Download, DownloadProgress, UserPreferences } from "@types";
|
||||||
import { GofileApi, QiwiApi, DatanodesApi, MediafireApi } from "../hosters";
|
import {
|
||||||
|
GofileApi,
|
||||||
|
QiwiApi,
|
||||||
|
DatanodesApi,
|
||||||
|
MediafireApi,
|
||||||
|
PixelDrainApi,
|
||||||
|
} from "../hosters";
|
||||||
import { PythonRPC } from "../python-rpc";
|
import { PythonRPC } from "../python-rpc";
|
||||||
import {
|
import {
|
||||||
LibtorrentPayload,
|
LibtorrentPayload,
|
||||||
@@ -283,11 +289,12 @@ export class DownloadManager {
|
|||||||
}
|
}
|
||||||
case Downloader.PixelDrain: {
|
case Downloader.PixelDrain: {
|
||||||
const id = download.uri.split("/").pop();
|
const id = download.uri.split("/").pop();
|
||||||
|
const downloadUrl = await PixelDrainApi.getDownloadUrl(id!);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
action: "start",
|
action: "start",
|
||||||
game_id: downloadId,
|
game_id: downloadId,
|
||||||
url: `https://cdn.pd5-gamedriveorg.workers.dev/api/file/${id}`,
|
url: downloadUrl,
|
||||||
save_path: download.downloadPath,
|
save_path: download.downloadPath,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import type {
|
|||||||
TorBoxAddTorrentRequest,
|
TorBoxAddTorrentRequest,
|
||||||
TorBoxRequestLinkRequest,
|
TorBoxRequestLinkRequest,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
|
import { appVersion } from "@main/constants";
|
||||||
|
|
||||||
export class TorBoxClient {
|
export class TorBoxClient {
|
||||||
private static instance: AxiosInstance;
|
private static instance: AxiosInstance;
|
||||||
@@ -18,6 +19,7 @@ export class TorBoxClient {
|
|||||||
baseURL: this.baseURL,
|
baseURL: this.baseURL,
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${apiToken}`,
|
Authorization: `Bearer ${apiToken}`,
|
||||||
|
"User-Agent": `Hydra/${appVersion}`,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +1,71 @@
|
|||||||
import axios, { AxiosResponse } from "axios";
|
import axios, { AxiosResponse } from "axios";
|
||||||
|
import { wrapper } from "axios-cookiejar-support";
|
||||||
|
import { CookieJar } from "tough-cookie";
|
||||||
|
|
||||||
export class DatanodesApi {
|
export class DatanodesApi {
|
||||||
private static readonly session = axios.create({});
|
private static readonly jar = new CookieJar();
|
||||||
|
|
||||||
|
private static readonly session = wrapper(
|
||||||
|
axios.create({
|
||||||
|
jar: DatanodesApi.jar,
|
||||||
|
withCredentials: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
public static async getDownloadUrl(downloadUrl: string): Promise<string> {
|
public static async getDownloadUrl(downloadUrl: string): Promise<string> {
|
||||||
const parsedUrl = new URL(downloadUrl);
|
try {
|
||||||
const pathSegments = parsedUrl.pathname.split("/");
|
const parsedUrl = new URL(downloadUrl);
|
||||||
|
const pathSegments = parsedUrl.pathname.split("/").filter(Boolean);
|
||||||
|
const fileCode = pathSegments[0];
|
||||||
|
|
||||||
const fileCode = decodeURIComponent(pathSegments[1]);
|
await this.jar.setCookie("lang=english;", "https://datanodes.to");
|
||||||
const fileName = decodeURIComponent(pathSegments[pathSegments.length - 1]);
|
|
||||||
|
|
||||||
const payload = new URLSearchParams({
|
const payload = new URLSearchParams({
|
||||||
op: "download2",
|
op: "download2",
|
||||||
id: fileCode,
|
id: fileCode,
|
||||||
rand: "",
|
method_free: "Free Download >>",
|
||||||
referer: "https://datanodes.to/download",
|
dl: "1",
|
||||||
method_free: "Free Download >>",
|
});
|
||||||
method_premium: "",
|
|
||||||
adblock_detected: "",
|
|
||||||
});
|
|
||||||
|
|
||||||
const response: AxiosResponse = await this.session.post(
|
const response: AxiosResponse = await this.session.post(
|
||||||
"https://datanodes.to/download",
|
"https://datanodes.to/download",
|
||||||
payload,
|
payload,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/x-www-form-urlencoded",
|
"User-Agent":
|
||||||
Cookie: `lang=english; file_name=${fileName}; file_code=${fileCode};`,
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:135.0) Gecko/20100101 Firefox/135.0",
|
||||||
Host: "datanodes.to",
|
Referer: "https://datanodes.to/download",
|
||||||
Origin: "https://datanodes.to",
|
Origin: "https://datanodes.to",
|
||||||
Referer: "https://datanodes.to/download",
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
"User-Agent":
|
},
|
||||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
|
maxRedirects: 0,
|
||||||
},
|
validateStatus: (status: number) => status === 302 || status < 400,
|
||||||
maxRedirects: 0,
|
}
|
||||||
validateStatus: (status: number) => status === 302 || status < 400,
|
);
|
||||||
|
|
||||||
|
if (response.status === 302) {
|
||||||
|
return response.headers["location"];
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
|
||||||
if (response.status === 302) {
|
if (typeof response.data === "object" && response.data.url) {
|
||||||
return response.headers["location"];
|
return decodeURIComponent(response.data.url);
|
||||||
|
}
|
||||||
|
|
||||||
|
const htmlContent = String(response.data);
|
||||||
|
if (!htmlContent) {
|
||||||
|
throw new Error("Empty response received");
|
||||||
|
}
|
||||||
|
|
||||||
|
const downloadLinkRegex = /href=["'](https:\/\/[^"']+)["']/;
|
||||||
|
const downloadLinkMatch = downloadLinkRegex.exec(htmlContent);
|
||||||
|
if (downloadLinkMatch) {
|
||||||
|
return downloadLinkMatch[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Failed to get the download link");
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching download URL:", error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,3 +2,4 @@ export * from "./gofile";
|
|||||||
export * from "./qiwi";
|
export * from "./qiwi";
|
||||||
export * from "./datanodes";
|
export * from "./datanodes";
|
||||||
export * from "./mediafire";
|
export * from "./mediafire";
|
||||||
|
export * from "./pixeldrain";
|
||||||
|
|||||||
42
src/main/services/hosters/pixeldrain.ts
Normal file
42
src/main/services/hosters/pixeldrain.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export class PixelDrainApi {
|
||||||
|
private static readonly browserHeaders = {
|
||||||
|
"User-Agent":
|
||||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36",
|
||||||
|
Accept:
|
||||||
|
"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8",
|
||||||
|
"Accept-Language": "en-US,en;q=0.9",
|
||||||
|
"Accept-Encoding": "gzip, deflate, br",
|
||||||
|
DNT: "1",
|
||||||
|
Connection: "keep-alive",
|
||||||
|
"Sec-Fetch-Dest": "document",
|
||||||
|
"Sec-Fetch-Mode": "navigate",
|
||||||
|
"Sec-Fetch-Site": "none",
|
||||||
|
"Sec-Fetch-User": "?1",
|
||||||
|
};
|
||||||
|
|
||||||
|
public static async getDownloadUrl(fileId: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`https://pd.cybar.xyz/${fileId}`, {
|
||||||
|
headers: this.browserHeaders,
|
||||||
|
maxRedirects: 0,
|
||||||
|
validateStatus: (status) =>
|
||||||
|
status === 301 || status === 302 || status === 200,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
response.headers.location ||
|
||||||
|
response.status === 301 ||
|
||||||
|
response.status === 302
|
||||||
|
) {
|
||||||
|
return response.headers.location;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(`No redirect URL found (status: ${response.status})`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching PixelDrain URL:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@ import { UserNotLoggedInError, SubscriptionRequiredError } from "@shared";
|
|||||||
import { omit } from "lodash-es";
|
import { omit } from "lodash-es";
|
||||||
import { appVersion } from "@main/constants";
|
import { appVersion } from "@main/constants";
|
||||||
import { getUserData } from "./user/get-user-data";
|
import { getUserData } from "./user/get-user-data";
|
||||||
import { isFuture, isToday } from "date-fns";
|
|
||||||
import { db } from "@main/level";
|
import { db } from "@main/level";
|
||||||
import { levelKeys } from "@main/level/sublevels";
|
import { levelKeys } from "@main/level/sublevels";
|
||||||
import type { Auth, User } from "@types";
|
import type { Auth, User } from "@types";
|
||||||
@@ -47,8 +46,8 @@ export class HydraApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static hasActiveSubscription() {
|
private static hasActiveSubscription() {
|
||||||
const expiresAt = this.userAuth.subscription?.expiresAt;
|
const expiresAt = new Date(this.userAuth.subscription?.expiresAt ?? 0);
|
||||||
return expiresAt && (isFuture(expiresAt) || isToday(expiresAt));
|
return expiresAt > new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
static async handleExternalAuth(uri: string) {
|
static async handleExternalAuth(uri: string) {
|
||||||
|
|||||||
@@ -7,3 +7,4 @@ export * from "./process-watcher";
|
|||||||
export * from "./main-loop";
|
export * from "./main-loop";
|
||||||
export * from "./hydra-api";
|
export * from "./hydra-api";
|
||||||
export * from "./ludusavi";
|
export * from "./ludusavi";
|
||||||
|
export * from "./cloud-sync";
|
||||||
|
|||||||
@@ -3,18 +3,21 @@ import { DownloadManager } from "./download";
|
|||||||
import { watchProcesses } from "./process-watcher";
|
import { watchProcesses } from "./process-watcher";
|
||||||
import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager";
|
import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager";
|
||||||
import { UpdateManager } from "./update-manager";
|
import { UpdateManager } from "./update-manager";
|
||||||
|
import { watchFriendRequests } from "@main/events/profile/sync-friend-requests";
|
||||||
|
import { MAIN_LOOP_INTERVAL } from "@main/constants";
|
||||||
|
|
||||||
export const startMainLoop = async () => {
|
export const startMainLoop = async () => {
|
||||||
// eslint-disable-next-line no-constant-condition
|
// eslint-disable-next-line no-constant-condition
|
||||||
while (true) {
|
while (true) {
|
||||||
await Promise.allSettled([
|
await Promise.allSettled([
|
||||||
watchProcesses(),
|
watchProcesses(),
|
||||||
|
watchFriendRequests(),
|
||||||
DownloadManager.watchDownloads(),
|
DownloadManager.watchDownloads(),
|
||||||
AchievementWatcherManager.watchAchievements(),
|
AchievementWatcherManager.watchAchievements(),
|
||||||
DownloadManager.getSeedStatus(),
|
DownloadManager.getSeedStatus(),
|
||||||
UpdateManager.checkForUpdatePeriodically(),
|
UpdateManager.checkForUpdatePeriodically(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await sleep(1500);
|
await sleep(MAIN_LOOP_INTERVAL);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { logger } from "../logger";
|
|||||||
import { WindowManager } from "../window-manager";
|
import { WindowManager } from "../window-manager";
|
||||||
import type { Game, UserPreferences } from "@types";
|
import type { Game, UserPreferences } from "@types";
|
||||||
import { db, levelKeys } from "@main/level";
|
import { db, levelKeys } from "@main/level";
|
||||||
|
import { restartAndInstallUpdate } from "@main/events/autoupdater/restart-and-install-update";
|
||||||
|
|
||||||
async function downloadImage(url: string | null) {
|
async function downloadImage(url: string | null) {
|
||||||
if (!url) return undefined;
|
if (!url) return undefined;
|
||||||
@@ -72,10 +73,33 @@ export const publishNotificationUpdateReadyToInstall = async (
|
|||||||
ns: "notifications",
|
ns: "notifications",
|
||||||
}),
|
}),
|
||||||
icon: trayIcon,
|
icon: trayIcon,
|
||||||
}).show();
|
})
|
||||||
|
.on("click", () => {
|
||||||
|
restartAndInstallUpdate();
|
||||||
|
})
|
||||||
|
.show();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const publishNewFriendRequestNotification = async () => {};
|
export const publishNewFriendRequestNotification = async () => {
|
||||||
|
const userPreferences = await db.get<string, UserPreferences | null>(
|
||||||
|
levelKeys.userPreferences,
|
||||||
|
{
|
||||||
|
valueEncoding: "json",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!userPreferences?.friendRequestNotificationsEnabled) return;
|
||||||
|
|
||||||
|
new Notification({
|
||||||
|
title: t("new_friend_request_title", {
|
||||||
|
ns: "notifications",
|
||||||
|
}),
|
||||||
|
body: t("new_friend_request_description", {
|
||||||
|
ns: "notifications",
|
||||||
|
}),
|
||||||
|
icon: trayIcon,
|
||||||
|
}).show();
|
||||||
|
};
|
||||||
|
|
||||||
export const publishCombinedNewAchievementNotification = async (
|
export const publishCombinedNewAchievementNotification = async (
|
||||||
achievementCount,
|
achievementCount,
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import axios from "axios";
|
|||||||
import { exec } from "child_process";
|
import { exec } from "child_process";
|
||||||
import { ProcessPayload } from "./download/types";
|
import { ProcessPayload } from "./download/types";
|
||||||
import { gamesSublevel, levelKeys } from "@main/level";
|
import { gamesSublevel, levelKeys } from "@main/level";
|
||||||
|
import { t } from "i18next";
|
||||||
|
import { CloudSync } from "./cloud-sync";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
|
||||||
const commands = {
|
const commands = {
|
||||||
findWineDir: `lsof -c wine 2>/dev/null | grep '/drive_c/windows$' | head -n 1 | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}'`,
|
findWineDir: `lsof -c wine 2>/dev/null | grep '/drive_c/windows$' | head -n 1 | awk '{for(i=9;i<=NF;i++) printf "%s ", $i; print ""}'`,
|
||||||
@@ -225,6 +228,18 @@ function onOpenGame(game: Game) {
|
|||||||
|
|
||||||
if (game.remoteId) {
|
if (game.remoteId) {
|
||||||
updateGamePlaytime(game, 0, new Date()).catch(() => {});
|
updateGamePlaytime(game, 0, new Date()).catch(() => {});
|
||||||
|
|
||||||
|
if (game.automaticCloudSync) {
|
||||||
|
CloudSync.uploadSaveGame(
|
||||||
|
game.objectId,
|
||||||
|
game.shop,
|
||||||
|
null,
|
||||||
|
t("automatic_backup_from", {
|
||||||
|
ns: "game_details",
|
||||||
|
date: format(new Date(), "dd/MM/yyyy"),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
createGame({ ...game, lastTimePlayed: new Date() }).catch(() => {});
|
createGame({ ...game, lastTimePlayed: new Date() }).catch(() => {});
|
||||||
}
|
}
|
||||||
@@ -287,6 +302,18 @@ const onCloseGame = (game: Game) => {
|
|||||||
performance.now() - gamePlaytime.lastSyncTick,
|
performance.now() - gamePlaytime.lastSyncTick,
|
||||||
game.lastTimePlayed!
|
game.lastTimePlayed!
|
||||||
).catch(() => {});
|
).catch(() => {});
|
||||||
|
|
||||||
|
if (game.automaticCloudSync) {
|
||||||
|
CloudSync.uploadSaveGame(
|
||||||
|
game.objectId,
|
||||||
|
game.shop,
|
||||||
|
null,
|
||||||
|
t("automatic_backup_from", {
|
||||||
|
ns: "game_details",
|
||||||
|
date: format(new Date(), "dd/MM/yyyy"),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
createGame(game).catch(() => {});
|
createGame(game).catch(() => {});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import updater, { UpdateInfo } from "electron-updater";
|
import updater, { UpdateInfo } from "electron-updater";
|
||||||
import { logger, WindowManager } from "@main/services";
|
import { logger, WindowManager } from "@main/services";
|
||||||
import { AppUpdaterEvent } from "@types";
|
import { AppUpdaterEvent, UserPreferences } from "@types";
|
||||||
import { app } from "electron";
|
import { app } from "electron";
|
||||||
import { publishNotificationUpdateReadyToInstall } from "@main/services/notifications";
|
import { publishNotificationUpdateReadyToInstall } from "@main/services/notifications";
|
||||||
|
import { db, levelKeys } from "@main/level";
|
||||||
const isAutoInstallAvailable =
|
import { MAIN_LOOP_INTERVAL } from "@main/constants";
|
||||||
process.platform !== "darwin" && process.env.PORTABLE_EXECUTABLE_FILE == null;
|
|
||||||
|
|
||||||
const { autoUpdater } = updater;
|
const { autoUpdater } = updater;
|
||||||
const sendEventsForDebug = false;
|
const sendEventsForDebug = false;
|
||||||
|
const ticksToUpdate = (50 * 60 * 1000) / MAIN_LOOP_INTERVAL; // 50 minutes
|
||||||
|
|
||||||
export class UpdateManager {
|
export class UpdateManager {
|
||||||
private static hasNotified = false;
|
private static hasNotified = false;
|
||||||
@@ -16,7 +16,7 @@ export class UpdateManager {
|
|||||||
private static checkTick = 0;
|
private static checkTick = 0;
|
||||||
|
|
||||||
private static mockValuesForDebug() {
|
private static mockValuesForDebug() {
|
||||||
this.sendEvent({ type: "update-available", info: { version: "1.3.0" } });
|
this.sendEvent({ type: "update-available", info: { version: "3.3.1" } });
|
||||||
this.sendEvent({ type: "update-downloaded" });
|
this.sendEvent({ type: "update-downloaded" });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,7 +24,27 @@ export class UpdateManager {
|
|||||||
WindowManager.mainWindow?.webContents.send("autoUpdaterEvent", event);
|
WindowManager.mainWindow?.webContents.send("autoUpdaterEvent", event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static checkForUpdates() {
|
private static async isAutoInstallEnabled() {
|
||||||
|
if (process.platform === "darwin") return false;
|
||||||
|
if (process.platform === "win32") {
|
||||||
|
return process.env.PORTABLE_EXECUTABLE_FILE == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.platform === "linux") {
|
||||||
|
const userPreferences = await db.get<string, UserPreferences | null>(
|
||||||
|
levelKeys.userPreferences,
|
||||||
|
{
|
||||||
|
valueEncoding: "json",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return userPreferences?.enableAutoInstall === true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async checkForUpdates() {
|
||||||
autoUpdater
|
autoUpdater
|
||||||
.once("update-available", (info: UpdateInfo) => {
|
.once("update-available", (info: UpdateInfo) => {
|
||||||
this.sendEvent({ type: "update-available", info });
|
this.sendEvent({ type: "update-available", info });
|
||||||
@@ -39,6 +59,8 @@ export class UpdateManager {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isAutoInstallAvailable = await this.isAutoInstallEnabled();
|
||||||
|
|
||||||
if (app.isPackaged) {
|
if (app.isPackaged) {
|
||||||
autoUpdater.autoDownload = isAutoInstallAvailable;
|
autoUpdater.autoDownload = isAutoInstallAvailable;
|
||||||
autoUpdater.checkForUpdates().then((result) => {
|
autoUpdater.checkForUpdates().then((result) => {
|
||||||
@@ -52,7 +74,7 @@ export class UpdateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static checkForUpdatePeriodically() {
|
public static checkForUpdatePeriodically() {
|
||||||
if (this.checkTick % 2000 == 0) {
|
if (this.checkTick % ticksToUpdate == 0) {
|
||||||
this.checkForUpdates();
|
this.checkForUpdates();
|
||||||
}
|
}
|
||||||
this.checkTick++;
|
this.checkTick++;
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { HydraApi } from "./hydra-api";
|
|||||||
import UserAgent from "user-agents";
|
import UserAgent from "user-agents";
|
||||||
import { db, gamesSublevel, levelKeys } from "@main/level";
|
import { db, gamesSublevel, levelKeys } from "@main/level";
|
||||||
import { slice, sortBy } from "lodash-es";
|
import { slice, sortBy } from "lodash-es";
|
||||||
import type { UserPreferences } from "@types";
|
import type { ScreenState, UserPreferences } from "@types";
|
||||||
import { AuthPage } from "@shared";
|
import { AuthPage } from "@shared";
|
||||||
import { isStaging } from "@main/constants";
|
import { isStaging } from "@main/constants";
|
||||||
|
|
||||||
@@ -26,27 +26,8 @@ export class WindowManager {
|
|||||||
|
|
||||||
private static readonly editorWindows: Map<string, BrowserWindow> = new Map();
|
private static readonly editorWindows: Map<string, BrowserWindow> = new Map();
|
||||||
|
|
||||||
private static loadMainWindowURL(hash = "") {
|
private static initialConfigInitializationMainWindow: Electron.BrowserWindowConstructorOptions =
|
||||||
// HMR for renderer base on electron-vite cli.
|
{
|
||||||
// Load the remote URL for development or the local html file for production.
|
|
||||||
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
|
|
||||||
this.mainWindow?.loadURL(
|
|
||||||
`${process.env["ELECTRON_RENDERER_URL"]}#/${hash}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
this.mainWindow?.loadFile(
|
|
||||||
path.join(__dirname, "../renderer/index.html"),
|
|
||||||
{
|
|
||||||
hash,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static createMainWindow() {
|
|
||||||
if (this.mainWindow) return;
|
|
||||||
|
|
||||||
this.mainWindow = new BrowserWindow({
|
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 720,
|
height: 720,
|
||||||
minWidth: 1024,
|
minWidth: 1024,
|
||||||
@@ -65,7 +46,65 @@ export class WindowManager {
|
|||||||
sandbox: false,
|
sandbox: false,
|
||||||
},
|
},
|
||||||
show: false,
|
show: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
private static loadMainWindowURL(hash = "") {
|
||||||
|
// HMR for renderer base on electron-vite cli.
|
||||||
|
// Load the remote URL for development or the local html file for production.
|
||||||
|
if (is.dev && process.env["ELECTRON_RENDERER_URL"]) {
|
||||||
|
this.mainWindow?.loadURL(
|
||||||
|
`${process.env["ELECTRON_RENDERER_URL"]}#/${hash}`
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.mainWindow?.loadFile(
|
||||||
|
path.join(__dirname, "../renderer/index.html"),
|
||||||
|
{
|
||||||
|
hash,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async saveScreenConfig(configScreenWhenClosed: ScreenState) {
|
||||||
|
await db.put(levelKeys.screenState, configScreenWhenClosed, {
|
||||||
|
valueEncoding: "json",
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async loadScreenConfig() {
|
||||||
|
const data = await db.get<string, ScreenState | undefined>(
|
||||||
|
levelKeys.screenState,
|
||||||
|
{
|
||||||
|
valueEncoding: "json",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
return data ?? { isMaximized: false, height: 720, width: 1200 };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static updateInitialConfig(
|
||||||
|
newConfig: Partial<Electron.BrowserWindowConstructorOptions>
|
||||||
|
) {
|
||||||
|
this.initialConfigInitializationMainWindow = {
|
||||||
|
...this.initialConfigInitializationMainWindow,
|
||||||
|
...newConfig,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async createMainWindow() {
|
||||||
|
if (this.mainWindow) return;
|
||||||
|
|
||||||
|
const { isMaximized = false, ...configWithoutMaximized } =
|
||||||
|
await this.loadScreenConfig();
|
||||||
|
|
||||||
|
this.updateInitialConfig(configWithoutMaximized);
|
||||||
|
|
||||||
|
this.mainWindow = new BrowserWindow(
|
||||||
|
this.initialConfigInitializationMainWindow
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isMaximized) {
|
||||||
|
this.mainWindow.maximize();
|
||||||
|
}
|
||||||
|
|
||||||
this.mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
|
this.mainWindow.webContents.session.webRequest.onBeforeSendHeaders(
|
||||||
(details, callback) => {
|
(details, callback) => {
|
||||||
@@ -143,9 +182,26 @@ export class WindowManager {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (this.mainWindow) {
|
||||||
|
const lastBounds = this.mainWindow.getBounds();
|
||||||
|
const isMaximized = this.mainWindow.isMaximized() ?? false;
|
||||||
|
const screenConfig = isMaximized
|
||||||
|
? {
|
||||||
|
x: undefined,
|
||||||
|
y: undefined,
|
||||||
|
height: this.initialConfigInitializationMainWindow.height ?? 720,
|
||||||
|
width: this.initialConfigInitializationMainWindow.width ?? 1200,
|
||||||
|
isMaximized: true,
|
||||||
|
}
|
||||||
|
: { ...lastBounds, isMaximized };
|
||||||
|
|
||||||
|
await this.saveScreenConfig(screenConfig);
|
||||||
|
}
|
||||||
|
|
||||||
if (userPreferences?.preferQuitInsteadOfHiding) {
|
if (userPreferences?.preferQuitInsteadOfHiding) {
|
||||||
app.quit();
|
app.quit();
|
||||||
}
|
}
|
||||||
|
|
||||||
WindowManager.mainWindow?.setProgressBar(-1);
|
WindowManager.mainWindow?.setProgressBar(-1);
|
||||||
WindowManager.mainWindow = null;
|
WindowManager.mainWindow = null;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import type {
|
|||||||
SeedingStatus,
|
SeedingStatus,
|
||||||
GameAchievement,
|
GameAchievement,
|
||||||
Theme,
|
Theme,
|
||||||
|
FriendRequestSync,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
import type { AuthPage, CatalogueCategory } from "@shared";
|
import type { AuthPage, CatalogueCategory } from "@shared";
|
||||||
import type { AxiosProgressEvent } from "axios";
|
import type { AxiosProgressEvent } from "axios";
|
||||||
@@ -101,6 +102,17 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.invoke("putDownloadSource", objectIds),
|
ipcRenderer.invoke("putDownloadSource", objectIds),
|
||||||
|
|
||||||
/* Library */
|
/* Library */
|
||||||
|
toggleAutomaticCloudSync: (
|
||||||
|
shop: GameShop,
|
||||||
|
objectId: string,
|
||||||
|
automaticCloudSync: boolean
|
||||||
|
) =>
|
||||||
|
ipcRenderer.invoke(
|
||||||
|
"toggleAutomaticCloudSync",
|
||||||
|
shop,
|
||||||
|
objectId,
|
||||||
|
automaticCloudSync
|
||||||
|
),
|
||||||
addGameToLibrary: (shop: GameShop, objectId: string, title: string) =>
|
addGameToLibrary: (shop: GameShop, objectId: string, title: string) =>
|
||||||
ipcRenderer.invoke("addGameToLibrary", shop, objectId, title),
|
ipcRenderer.invoke("addGameToLibrary", shop, objectId, title),
|
||||||
createGameShortcut: (shop: GameShop, objectId: string) =>
|
createGameShortcut: (shop: GameShop, objectId: string) =>
|
||||||
@@ -266,6 +278,7 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
showItemInFolder: (path: string) =>
|
showItemInFolder: (path: string) =>
|
||||||
ipcRenderer.invoke("showItemInFolder", path),
|
ipcRenderer.invoke("showItemInFolder", path),
|
||||||
getFeatures: () => ipcRenderer.invoke("getFeatures"),
|
getFeatures: () => ipcRenderer.invoke("getFeatures"),
|
||||||
|
getBadges: () => ipcRenderer.invoke("getBadges"),
|
||||||
platform: process.platform,
|
platform: process.platform,
|
||||||
|
|
||||||
/* Auto update */
|
/* Auto update */
|
||||||
@@ -294,6 +307,15 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.invoke("processProfileImage", imagePath),
|
ipcRenderer.invoke("processProfileImage", imagePath),
|
||||||
getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"),
|
getFriendRequests: () => ipcRenderer.invoke("getFriendRequests"),
|
||||||
syncFriendRequests: () => ipcRenderer.invoke("syncFriendRequests"),
|
syncFriendRequests: () => ipcRenderer.invoke("syncFriendRequests"),
|
||||||
|
onSyncFriendRequests: (cb: (friendRequests: FriendRequestSync) => void) => {
|
||||||
|
const listener = (
|
||||||
|
_event: Electron.IpcRendererEvent,
|
||||||
|
friendRequests: FriendRequestSync
|
||||||
|
) => cb(friendRequests);
|
||||||
|
ipcRenderer.on("on-sync-friend-requests", listener);
|
||||||
|
return () =>
|
||||||
|
ipcRenderer.removeListener("on-sync-friend-requests", listener);
|
||||||
|
},
|
||||||
updateFriendRequest: (userId: string, action: FriendRequestAction) =>
|
updateFriendRequest: (userId: string, action: FriendRequestAction) =>
|
||||||
ipcRenderer.invoke("updateFriendRequest", userId, action),
|
ipcRenderer.invoke("updateFriendRequest", userId, action),
|
||||||
sendFriendRequest: (userId: string) =>
|
sendFriendRequest: (userId: string) =>
|
||||||
@@ -325,6 +347,7 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.invoke("getUnlockedAchievements", objectId, shop),
|
ipcRenderer.invoke("getUnlockedAchievements", objectId, shop),
|
||||||
|
|
||||||
/* Auth */
|
/* Auth */
|
||||||
|
getAuth: () => ipcRenderer.invoke("getAuth"),
|
||||||
signOut: () => ipcRenderer.invoke("signOut"),
|
signOut: () => ipcRenderer.invoke("signOut"),
|
||||||
openAuthWindow: (page: AuthPage) =>
|
openAuthWindow: (page: AuthPage) =>
|
||||||
ipcRenderer.invoke("openAuthWindow", page),
|
ipcRenderer.invoke("openAuthWindow", page),
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
setUserDetails,
|
setUserDetails,
|
||||||
setProfileBackground,
|
setProfileBackground,
|
||||||
setGameRunning,
|
setGameRunning,
|
||||||
|
setFriendRequestCount,
|
||||||
} from "@renderer/features";
|
} from "@renderer/features";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { UserFriendModal } from "./pages/shared-modals/user-friend-modal";
|
import { UserFriendModal } from "./pages/shared-modals/user-friend-modal";
|
||||||
@@ -51,7 +52,6 @@ export function App() {
|
|||||||
isFriendsModalVisible,
|
isFriendsModalVisible,
|
||||||
friendRequetsModalTab,
|
friendRequetsModalTab,
|
||||||
friendModalUserId,
|
friendModalUserId,
|
||||||
syncFriendRequests,
|
|
||||||
hideFriendsModal,
|
hideFriendsModal,
|
||||||
fetchUserDetails,
|
fetchUserDetails,
|
||||||
updateUserDetails,
|
updateUserDetails,
|
||||||
@@ -123,7 +123,7 @@ export function App() {
|
|||||||
.then((response) => {
|
.then((response) => {
|
||||||
if (response) {
|
if (response) {
|
||||||
updateUserDetails(response);
|
updateUserDetails(response);
|
||||||
syncFriendRequests();
|
window.electron.syncFriendRequests();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -134,23 +134,27 @@ export function App() {
|
|||||||
$script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}/bundle.js?t=${Date.now()}`;
|
$script.src = `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}/bundle.js?t=${Date.now()}`;
|
||||||
document.head.appendChild($script);
|
document.head.appendChild($script);
|
||||||
});
|
});
|
||||||
}, [fetchUserDetails, syncFriendRequests, updateUserDetails, dispatch]);
|
}, [fetchUserDetails, updateUserDetails, dispatch]);
|
||||||
|
|
||||||
const onSignIn = useCallback(() => {
|
const onSignIn = useCallback(() => {
|
||||||
fetchUserDetails().then((response) => {
|
fetchUserDetails().then((response) => {
|
||||||
if (response) {
|
if (response) {
|
||||||
updateUserDetails(response);
|
updateUserDetails(response);
|
||||||
syncFriendRequests();
|
window.electron.syncFriendRequests();
|
||||||
showSuccessToast(t("successfully_signed_in"));
|
showSuccessToast(t("successfully_signed_in"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}, [
|
}, [fetchUserDetails, t, showSuccessToast, updateUserDetails]);
|
||||||
fetchUserDetails,
|
|
||||||
syncFriendRequests,
|
useEffect(() => {
|
||||||
t,
|
const unsubscribe = window.electron.onSyncFriendRequests((result) => {
|
||||||
showSuccessToast,
|
dispatch(setFriendRequestCount(result.friendRequestCount));
|
||||||
updateUserDetails,
|
});
|
||||||
]);
|
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
};
|
||||||
|
}, [dispatch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const unsubscribe = window.electron.onGamesRunning((gamesRunning) => {
|
const unsubscribe = window.electron.onGamesRunning((gamesRunning) => {
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
<svg width="240" height="246" viewBox="0 0 240 246" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
||||||
<path d="M117.681 8.44054C120.27 4.75592 125.73 4.75592 128.319 8.44054L149.273 38.2669C151.08 40.8399 154.301 42.0121 157.339 41.203L192.563 31.8236C196.914 30.6649 201.097 34.1747 200.712 38.6612L197.591 74.9784C197.322 78.1113 199.036 81.0795 201.884 82.4128L234.895 97.8691C238.974 99.7785 239.922 105.156 236.743 108.345L211.008 134.16C208.788 136.387 208.193 139.762 209.517 142.614L224.871 175.674C226.767 179.758 224.037 184.486 219.552 184.886L183.245 188.119C180.113 188.398 177.487 190.601 176.669 193.637L167.18 228.832C166.007 233.179 160.876 235.047 157.184 232.47L127.292 211.609C124.714 209.809 121.286 209.809 118.708 211.609L88.8163 232.47C85.1236 235.047 79.9927 233.179 78.8204 228.832L69.3314 193.637C68.5129 190.601 65.8873 188.398 62.7553 188.119L26.4479 184.886C21.9627 184.486 19.2326 179.758 21.1293 175.674L36.4827 142.614C37.8072 139.762 37.212 136.387 34.992 134.16L9.25738 108.345C6.07823 105.156 7.02639 99.7785 11.1045 97.8691L44.1164 82.4128C46.9642 81.0795 48.6778 78.1113 48.4087 74.9784L45.2883 38.6611C44.9028 34.1747 49.0856 30.6649 53.437 31.8236L88.6606 41.203C91.6992 42.0121 94.9198 40.8399 96.7274 38.2669L117.681 8.44054Z" fill="url(#paint0_linear_1378_2496)"/>
|
|
||||||
<path d="M117.681 8.44054C120.27 4.75592 125.73 4.75592 128.319 8.44054L149.273 38.2669C151.08 40.8399 154.301 42.0121 157.339 41.203L192.563 31.8236C196.914 30.6649 201.097 34.1747 200.712 38.6612L197.591 74.9784C197.322 78.1113 199.036 81.0795 201.884 82.4128L234.895 97.8691C238.974 99.7785 239.922 105.156 236.743 108.345L211.008 134.16C208.788 136.387 208.193 139.762 209.517 142.614L224.871 175.674C226.767 179.758 224.037 184.486 219.552 184.886L183.245 188.119C180.113 188.398 177.487 190.601 176.669 193.637L167.18 228.832C166.007 233.179 160.876 235.047 157.184 232.47L127.292 211.609C124.714 209.809 121.286 209.809 118.708 211.609L88.8163 232.47C85.1236 235.047 79.9927 233.179 78.8204 228.832L69.3314 193.637C68.5129 190.601 65.8873 188.398 62.7553 188.119L26.4479 184.886C21.9627 184.486 19.2326 179.758 21.1293 175.674L36.4827 142.614C37.8072 139.762 37.212 136.387 34.992 134.16L9.25738 108.345C6.07823 105.156 7.02639 99.7785 11.1045 97.8691L44.1164 82.4128C46.9642 81.0795 48.6778 78.1113 48.4087 74.9784L45.2883 38.6611C44.9028 34.1747 49.0856 30.6649 53.437 31.8236L88.6606 41.203C91.6992 42.0121 94.9198 40.8399 96.7274 38.2669L117.681 8.44054Z" fill="url(#paint1_linear_1378_2496)"/>
|
|
||||||
<path d="M117.681 8.44054C120.27 4.75592 125.73 4.75592 128.319 8.44054L149.273 38.2669C151.08 40.8399 154.301 42.0121 157.339 41.203L192.563 31.8236C196.914 30.6649 201.097 34.1747 200.712 38.6612L197.591 74.9784C197.322 78.1113 199.036 81.0795 201.884 82.4128L234.895 97.8691C238.974 99.7785 239.922 105.156 236.743 108.345L211.008 134.16C208.788 136.387 208.193 139.762 209.517 142.614L224.871 175.674C226.767 179.758 224.037 184.486 219.552 184.886L183.245 188.119C180.113 188.398 177.487 190.601 176.669 193.637L167.18 228.832C166.007 233.179 160.876 235.047 157.184 232.47L127.292 211.609C124.714 209.809 121.286 209.809 118.708 211.609L88.8163 232.47C85.1236 235.047 79.9927 233.179 78.8204 228.832L69.3314 193.637C68.5129 190.601 65.8873 188.398 62.7553 188.119L26.4479 184.886C21.9627 184.486 19.2326 179.758 21.1293 175.674L36.4827 142.614C37.8072 139.762 37.212 136.387 34.992 134.16L9.25738 108.345C6.07823 105.156 7.02639 99.7785 11.1045 97.8691L44.1164 82.4128C46.9642 81.0795 48.6778 78.1113 48.4087 74.9784L45.2883 38.6611C44.9028 34.1747 49.0856 30.6649 53.437 31.8236L88.6606 41.203C91.6992 42.0121 94.9198 40.8399 96.7274 38.2669L117.681 8.44054Z" stroke="url(#paint2_linear_1378_2496)"/>
|
|
||||||
<g opacity="0.9">
|
|
||||||
<g style="mix-blend-mode:overlay">
|
|
||||||
<path d="M113.207 107.103L113.873 107.307L114.295 106.754C120.652 98.4238 129.53 87.9999 139.582 79.6678C149.661 71.3136 160.772 65.1865 171.609 65.1865C172.421 65.1865 173.2 65.5092 173.775 66.0835C174.349 66.6578 174.672 67.4368 174.672 68.249C174.672 79.0868 168.546 90.1972 160.192 100.276C151.861 110.328 141.437 119.207 133.105 125.563L132.551 125.985L132.755 126.651C134.104 131.057 134.402 135.718 133.623 140.26C132.844 144.802 131.011 149.098 128.271 152.803C125.531 156.508 121.96 159.519 117.845 161.594C113.73 163.668 109.186 164.749 104.578 164.749H65.9851C65.3296 164.748 64.6916 164.538 64.1649 164.147C63.6381 163.757 63.2505 163.208 63.059 162.581C62.8674 161.954 62.8821 161.283 63.1008 160.665C63.3195 160.047 63.7307 159.515 64.274 159.149L64.2813 159.144C64.8739 158.736 75.1093 151.439 75.1093 135.28C75.1093 130.672 76.19 126.128 78.2646 122.013C80.3393 117.898 83.35 114.328 87.055 111.587C90.76 108.847 95.056 107.014 99.598 106.235C104.14 105.457 108.801 105.754 113.207 107.103ZM120.25 109.05L119.585 109.911L120.499 110.501C124.047 112.792 127.067 115.811 129.357 119.359L129.948 120.275L130.809 119.608C133.07 117.857 135.204 116.141 137.212 114.46L137.904 113.881L137.399 113.133C134.556 108.926 130.933 105.302 126.725 102.459L125.979 101.955L125.4 102.645C123.714 104.65 121.997 106.785 120.25 109.05ZM141.909 108.878L142.566 109.805L143.402 109.036C161.044 92.7992 166.529 80.4502 168.029 72.9963L168.328 71.5152L166.848 71.8195C159.403 73.3505 147.056 78.8076 130.817 96.4511L130.048 97.2872L130.975 97.9442C135.21 100.946 138.907 104.643 141.909 108.878Z" fill="black"/>
|
|
||||||
<path d="M113.207 107.103L113.873 107.307L114.295 106.754C120.652 98.4238 129.53 87.9999 139.582 79.6678C149.661 71.3136 160.772 65.1865 171.609 65.1865C172.421 65.1865 173.2 65.5092 173.775 66.0835C174.349 66.6578 174.672 67.4368 174.672 68.249C174.672 79.0868 168.546 90.1972 160.192 100.276C151.861 110.328 141.437 119.207 133.105 125.563L132.551 125.985L132.755 126.651C134.104 131.057 134.402 135.718 133.623 140.26C132.844 144.802 131.011 149.098 128.271 152.803C125.531 156.508 121.96 159.519 117.845 161.594C113.73 163.668 109.186 164.749 104.578 164.749H65.9851C65.3296 164.748 64.6916 164.538 64.1649 164.147C63.6381 163.757 63.2505 163.208 63.059 162.581C62.8674 161.954 62.8821 161.283 63.1008 160.665C63.3195 160.047 63.7307 159.515 64.274 159.149L64.2813 159.144C64.8739 158.736 75.1093 151.439 75.1093 135.28C75.1093 130.672 76.19 126.128 78.2646 122.013C80.3393 117.898 83.35 114.328 87.055 111.587C90.76 108.847 95.056 107.014 99.598 106.235C104.14 105.457 108.801 105.754 113.207 107.103ZM120.25 109.05L119.585 109.911L120.499 110.501C124.047 112.792 127.067 115.811 129.357 119.359L129.948 120.275L130.809 119.608C133.07 117.857 135.204 116.141 137.212 114.46L137.904 113.881L137.399 113.133C134.556 108.926 130.933 105.302 126.725 102.459L125.979 101.955L125.4 102.645C123.714 104.65 121.997 106.785 120.25 109.05ZM141.909 108.878L142.566 109.805L143.402 109.036C161.044 92.7992 166.529 80.4502 168.029 72.9963L168.328 71.5152L166.848 71.8195C159.403 73.3505 147.056 78.8076 130.817 96.4511L130.048 97.2872L130.975 97.9442C135.21 100.946 138.907 104.643 141.909 108.878Z" stroke="url(#paint3_linear_1378_2496)" stroke-width="2"/>
|
|
||||||
</g>
|
|
||||||
</g>
|
|
||||||
<defs>
|
|
||||||
<linearGradient id="paint0_linear_1378_2496" x1="5.63736e-07" y1="12.92" x2="246" y2="233.08" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#0CF1CA"/>
|
|
||||||
<stop offset="1" stop-color="#1DCCEB"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint1_linear_1378_2496" x1="19.8951" y1="-3.50306e-06" x2="226.105" y2="246" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="#0DDEBB"/>
|
|
||||||
<stop offset="1" stop-color="#052520"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint2_linear_1378_2496" x1="-1.9947e-06" y1="18.0561" x2="246" y2="227.944" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="white" stop-opacity="0.7"/>
|
|
||||||
<stop offset="1" stop-color="white" stop-opacity="0.1"/>
|
|
||||||
</linearGradient>
|
|
||||||
<linearGradient id="paint3_linear_1378_2496" x1="61.9253" y1="71.6411" x2="164.664" y2="169.814" gradientUnits="userSpaceOnUse">
|
|
||||||
<stop stop-color="white"/>
|
|
||||||
<stop offset="1" stop-color="white" stop-opacity="0.2"/>
|
|
||||||
</linearGradient>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
|
||||||
|
Before Width: | Height: | Size: 7.8 KiB |
@@ -10,6 +10,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: globals.$muted-color;
|
color: globals.$muted-color;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
&__image {
|
&__image {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { PersonIcon } from "@primer/octicons-react";
|
import { PersonIcon } from "@primer/octicons-react";
|
||||||
|
import cn from "classnames";
|
||||||
|
|
||||||
import "./avatar.scss";
|
import "./avatar.scss";
|
||||||
|
|
||||||
@@ -14,11 +15,18 @@ export interface AvatarProps
|
|||||||
src?: string | null;
|
src?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Avatar({ size, alt, src, ...props }: AvatarProps) {
|
export function Avatar({ size, alt, src, className, ...props }: AvatarProps) {
|
||||||
return (
|
return (
|
||||||
<div className="profile-avatar" style={{ width: size, height: size }}>
|
<div className="profile-avatar" style={{ width: size, height: size }}>
|
||||||
{src ? (
|
{src ? (
|
||||||
<img className="profile-avatar__image" alt={alt} src={src} {...props} />
|
<img
|
||||||
|
className={cn("profile-avatar__image", className)}
|
||||||
|
alt={alt}
|
||||||
|
src={src}
|
||||||
|
width={size}
|
||||||
|
height={size}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<PersonIcon size={size * 0.7} />
|
<PersonIcon size={size * 0.7} />
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export interface CheckboxFieldProps
|
|||||||
React.InputHTMLAttributes<HTMLInputElement>,
|
React.InputHTMLAttributes<HTMLInputElement>,
|
||||||
HTMLInputElement
|
HTMLInputElement
|
||||||
> {
|
> {
|
||||||
label: string;
|
label: string | React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CheckboxField({ label, ...props }: CheckboxFieldProps) {
|
export function CheckboxField({ label, ...props }: CheckboxFieldProps) {
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export interface ConfirmationModalProps extends Omit<ModalProps, "children"> {
|
|||||||
|
|
||||||
onConfirm: () => void;
|
onConfirm: () => void;
|
||||||
onCancel?: () => void;
|
onCancel?: () => void;
|
||||||
|
|
||||||
|
buttonsIsDisabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function ConfirmationModal({
|
export function ConfirmationModal({
|
||||||
@@ -18,6 +20,7 @@ export function ConfirmationModal({
|
|||||||
descriptionText,
|
descriptionText,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
onCancel,
|
onCancel,
|
||||||
|
buttonsIsDisabled = false,
|
||||||
...props
|
...props
|
||||||
}: ConfirmationModalProps) {
|
}: ConfirmationModalProps) {
|
||||||
const handleCancelClick = () => {
|
const handleCancelClick = () => {
|
||||||
@@ -38,7 +41,11 @@ export function ConfirmationModal({
|
|||||||
<Button theme="outline" onClick={handleCancelClick}>
|
<Button theme="outline" onClick={handleCancelClick}>
|
||||||
{cancelButtonLabel}
|
{cancelButtonLabel}
|
||||||
</Button>
|
</Button>
|
||||||
<Button theme="danger" onClick={onConfirm}>
|
<Button
|
||||||
|
theme="danger"
|
||||||
|
disabled={buttonsIsDisabled}
|
||||||
|
onClick={onConfirm}
|
||||||
|
>
|
||||||
{confirmButtonLabel}
|
{confirmButtonLabel}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import "./game-card.scss";
|
|||||||
|
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Badge } from "../badge/badge";
|
import { Badge } from "../badge/badge";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState, useMemo } from "react";
|
||||||
import { useFormat, useRepacks } from "@renderer/hooks";
|
import { useFormat, useRepacks } from "@renderer/hooks";
|
||||||
import { steamUrlBuilder } from "@shared";
|
import { steamUrlBuilder } from "@shared";
|
||||||
|
|
||||||
@@ -45,6 +45,15 @@ export function GameCard({ game, ...props }: GameCardProps) {
|
|||||||
|
|
||||||
const { numberFormatter } = useFormat();
|
const { numberFormatter } = useFormat();
|
||||||
|
|
||||||
|
const firstThreeRepackers = useMemo(
|
||||||
|
() => uniqueRepackers.slice(0, 3),
|
||||||
|
[uniqueRepackers]
|
||||||
|
);
|
||||||
|
const remainingCount = useMemo(
|
||||||
|
() => uniqueRepackers.length - 3,
|
||||||
|
[uniqueRepackers]
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
{...props}
|
{...props}
|
||||||
@@ -68,15 +77,24 @@ export function GameCard({ game, ...props }: GameCardProps) {
|
|||||||
|
|
||||||
{uniqueRepackers.length > 0 ? (
|
{uniqueRepackers.length > 0 ? (
|
||||||
<ul className="game-card__download-options">
|
<ul className="game-card__download-options">
|
||||||
{uniqueRepackers.map((repacker) => (
|
{firstThreeRepackers.map((repacker) => (
|
||||||
<li key={repacker}>
|
<li key={repacker}>
|
||||||
<Badge>{repacker}</Badge>
|
<Badge>{repacker}</Badge>
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
{remainingCount > 0 && (
|
||||||
|
<li>
|
||||||
|
<Badge>
|
||||||
|
+{remainingCount}{" "}
|
||||||
|
{t("game_card:available", { count: remainingCount })}
|
||||||
|
</Badge>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
<p className="game-card__no-download-label">{t("no_downloads")}</p>
|
<p className="game-card__no-download-label">{t("no_downloads")}</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="game-card__specifics">
|
<div className="game-card__specifics">
|
||||||
<div className="game-card__specifics-item">
|
<div className="game-card__specifics-item">
|
||||||
<DownloadIcon />
|
<DownloadIcon />
|
||||||
@@ -84,11 +102,10 @@ export function GameCard({ game, ...props }: GameCardProps) {
|
|||||||
{stats ? numberFormatter.format(stats.downloadCount) : "…"}
|
{stats ? numberFormatter.format(stats.downloadCount) : "…"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="game-card__specifics-item">
|
<div className="game-card__specifics-item">
|
||||||
<PeopleIcon />
|
<PeopleIcon />
|
||||||
<span>
|
<span>
|
||||||
{stats ? numberFormatter.format(stats?.playerCount) : "…"}
|
{stats ? numberFormatter.format(stats.playerCount) : "…"}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { PeopleIcon } from "@primer/octicons-react";
|
import { PeopleIcon } from "@primer/octicons-react";
|
||||||
import { useAppSelector, useUserDetails } from "@renderer/hooks";
|
import { useAppSelector, useUserDetails } from "@renderer/hooks";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
||||||
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
import SteamLogo from "@renderer/assets/steam-logo.svg?react";
|
||||||
@@ -9,19 +9,13 @@ import { Avatar } from "../avatar/avatar";
|
|||||||
import { AuthPage } from "@shared";
|
import { AuthPage } from "@shared";
|
||||||
import "./sidebar-profile.scss";
|
import "./sidebar-profile.scss";
|
||||||
|
|
||||||
const LONG_POLLING_INTERVAL = 120_000;
|
|
||||||
|
|
||||||
export function SidebarProfile() {
|
export function SidebarProfile() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const { t } = useTranslation("sidebar");
|
const { t } = useTranslation("sidebar");
|
||||||
|
|
||||||
const {
|
const { userDetails, friendRequestCount, showFriendsModal } =
|
||||||
userDetails,
|
useUserDetails();
|
||||||
friendRequestCount,
|
|
||||||
showFriendsModal,
|
|
||||||
syncFriendRequests,
|
|
||||||
} = useUserDetails();
|
|
||||||
|
|
||||||
const { gameRunning } = useAppSelector((state) => state.gameRunning);
|
const { gameRunning } = useAppSelector((state) => state.gameRunning);
|
||||||
|
|
||||||
@@ -34,16 +28,6 @@ export function SidebarProfile() {
|
|||||||
navigate(`/profile/${userDetails.id}`);
|
navigate(`/profile/${userDetails.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const pollingInterval = setInterval(() => {
|
|
||||||
syncFriendRequests();
|
|
||||||
}, LONG_POLLING_INTERVAL);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearInterval(pollingInterval);
|
|
||||||
};
|
|
||||||
}, [syncFriendRequests]);
|
|
||||||
|
|
||||||
const friendsButton = useMemo(() => {
|
const friendsButton = useMemo(() => {
|
||||||
if (!userDetails) return null;
|
if (!userDetails) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { darkenColor } from "@renderer/helpers";
|
import { darkenColor } from "@renderer/helpers";
|
||||||
import { useAppSelector, useToast } from "@renderer/hooks";
|
import { useAppSelector, useToast } from "@renderer/hooks";
|
||||||
import type { UserProfile, UserStats } from "@types";
|
import type { Badge, UserProfile, UserStats } from "@types";
|
||||||
import { average } from "color.js";
|
import { average } from "color.js";
|
||||||
|
|
||||||
import { createContext, useCallback, useEffect, useState } from "react";
|
import { createContext, useCallback, useEffect, useState } from "react";
|
||||||
@@ -16,6 +16,7 @@ export interface UserProfileContext {
|
|||||||
getUserProfile: () => Promise<void>;
|
getUserProfile: () => Promise<void>;
|
||||||
setSelectedBackgroundImage: React.Dispatch<React.SetStateAction<string>>;
|
setSelectedBackgroundImage: React.Dispatch<React.SetStateAction<string>>;
|
||||||
backgroundImage: string;
|
backgroundImage: string;
|
||||||
|
badges: Badge[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_USER_PROFILE_BACKGROUND = "#151515B3";
|
export const DEFAULT_USER_PROFILE_BACKGROUND = "#151515B3";
|
||||||
@@ -28,6 +29,7 @@ export const userProfileContext = createContext<UserProfileContext>({
|
|||||||
getUserProfile: async () => {},
|
getUserProfile: async () => {},
|
||||||
setSelectedBackgroundImage: () => {},
|
setSelectedBackgroundImage: () => {},
|
||||||
backgroundImage: "",
|
backgroundImage: "",
|
||||||
|
badges: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const { Provider } = userProfileContext;
|
const { Provider } = userProfileContext;
|
||||||
@@ -47,6 +49,7 @@ export function UserProfileContextProvider({
|
|||||||
const [userStats, setUserStats] = useState<UserStats | null>(null);
|
const [userStats, setUserStats] = useState<UserStats | null>(null);
|
||||||
|
|
||||||
const [userProfile, setUserProfile] = useState<UserProfile | null>(null);
|
const [userProfile, setUserProfile] = useState<UserProfile | null>(null);
|
||||||
|
const [badges, setBadges] = useState<Badge[]>([]);
|
||||||
const [heroBackground, setHeroBackground] = useState(
|
const [heroBackground, setHeroBackground] = useState(
|
||||||
DEFAULT_USER_PROFILE_BACKGROUND
|
DEFAULT_USER_PROFILE_BACKGROUND
|
||||||
);
|
);
|
||||||
@@ -101,12 +104,18 @@ export function UserProfileContextProvider({
|
|||||||
});
|
});
|
||||||
}, [navigate, getUserStats, showErrorToast, userId, t]);
|
}, [navigate, getUserStats, showErrorToast, userId, t]);
|
||||||
|
|
||||||
|
const getBadges = useCallback(async () => {
|
||||||
|
const badges = await window.electron.getBadges();
|
||||||
|
setBadges(badges);
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUserProfile(null);
|
setUserProfile(null);
|
||||||
setHeroBackground(DEFAULT_USER_PROFILE_BACKGROUND);
|
setHeroBackground(DEFAULT_USER_PROFILE_BACKGROUND);
|
||||||
|
|
||||||
getUserProfile();
|
getUserProfile();
|
||||||
}, [getUserProfile]);
|
getBadges();
|
||||||
|
}, [getUserProfile, getBadges]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider
|
<Provider
|
||||||
@@ -118,6 +127,7 @@ export function UserProfileContextProvider({
|
|||||||
setSelectedBackgroundImage,
|
setSelectedBackgroundImage,
|
||||||
backgroundImage: getBackgroundImageUrl(),
|
backgroundImage: getBackgroundImageUrl(),
|
||||||
userStats,
|
userStats,
|
||||||
|
badges,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
14
src/renderer/src/declaration.d.ts
vendored
14
src/renderer/src/declaration.d.ts
vendored
@@ -30,6 +30,8 @@ import type {
|
|||||||
GameRunning,
|
GameRunning,
|
||||||
TorBoxUser,
|
TorBoxUser,
|
||||||
Theme,
|
Theme,
|
||||||
|
Badge,
|
||||||
|
Auth,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
import type { AxiosProgressEvent } from "axios";
|
import type { AxiosProgressEvent } from "axios";
|
||||||
import type disk from "diskusage";
|
import type disk from "diskusage";
|
||||||
@@ -86,6 +88,11 @@ declare global {
|
|||||||
getDevelopers: () => Promise<string[]>;
|
getDevelopers: () => Promise<string[]>;
|
||||||
|
|
||||||
/* Library */
|
/* Library */
|
||||||
|
toggleAutomaticCloudSync: (
|
||||||
|
shop: GameShop,
|
||||||
|
objectId: string,
|
||||||
|
automaticCloudSync: boolean
|
||||||
|
) => Promise<void>;
|
||||||
addGameToLibrary: (
|
addGameToLibrary: (
|
||||||
shop: GameShop,
|
shop: GameShop,
|
||||||
objectId: string,
|
objectId: string,
|
||||||
@@ -217,6 +224,7 @@ declare global {
|
|||||||
) => Promise<Electron.OpenDialogReturnValue>;
|
) => Promise<Electron.OpenDialogReturnValue>;
|
||||||
showItemInFolder: (path: string) => Promise<void>;
|
showItemInFolder: (path: string) => Promise<void>;
|
||||||
getFeatures: () => Promise<string[]>;
|
getFeatures: () => Promise<string[]>;
|
||||||
|
getBadges: () => Promise<Badge[]>;
|
||||||
platform: NodeJS.Platform;
|
platform: NodeJS.Platform;
|
||||||
|
|
||||||
/* Auto update */
|
/* Auto update */
|
||||||
@@ -227,6 +235,7 @@ declare global {
|
|||||||
restartAndInstallUpdate: () => Promise<void>;
|
restartAndInstallUpdate: () => Promise<void>;
|
||||||
|
|
||||||
/* Auth */
|
/* Auth */
|
||||||
|
getAuth: () => Promise<Auth | null>;
|
||||||
signOut: () => Promise<void>;
|
signOut: () => Promise<void>;
|
||||||
openAuthWindow: (page: AuthPage) => Promise<void>;
|
openAuthWindow: (page: AuthPage) => Promise<void>;
|
||||||
getSessionHash: () => Promise<string | null>;
|
getSessionHash: () => Promise<string | null>;
|
||||||
@@ -271,7 +280,10 @@ declare global {
|
|||||||
path: string
|
path: string
|
||||||
) => Promise<{ imagePath: string; mimeType: string }>;
|
) => Promise<{ imagePath: string; mimeType: string }>;
|
||||||
getFriendRequests: () => Promise<FriendRequest[]>;
|
getFriendRequests: () => Promise<FriendRequest[]>;
|
||||||
syncFriendRequests: () => Promise<FriendRequestSync>;
|
syncFriendRequests: () => Promise<void>;
|
||||||
|
onSyncFriendRequests: (
|
||||||
|
cb: (friendRequests: FriendRequestSync) => void
|
||||||
|
) => () => Electron.IpcRenderer;
|
||||||
updateFriendRequest: (
|
updateFriendRequest: (
|
||||||
userId: string,
|
userId: string,
|
||||||
action: FriendRequestAction
|
action: FriendRequestAction
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import {
|
|||||||
setFriendRequests,
|
setFriendRequests,
|
||||||
setFriendsModalVisible,
|
setFriendsModalVisible,
|
||||||
setFriendsModalHidden,
|
setFriendsModalHidden,
|
||||||
setFriendRequestCount,
|
|
||||||
} from "@renderer/features";
|
} from "@renderer/features";
|
||||||
import type {
|
import type {
|
||||||
FriendRequestAction,
|
FriendRequestAction,
|
||||||
@@ -15,7 +14,6 @@ import type {
|
|||||||
} from "@types";
|
} from "@types";
|
||||||
import * as Sentry from "@sentry/react";
|
import * as Sentry from "@sentry/react";
|
||||||
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal";
|
||||||
import { isFuture, isToday } from "date-fns";
|
|
||||||
|
|
||||||
export function useUserDetails() {
|
export function useUserDetails() {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@@ -89,24 +87,15 @@ export function useUserDetails() {
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const syncFriendRequests = useCallback(async () => {
|
|
||||||
return window.electron
|
|
||||||
.syncFriendRequests()
|
|
||||||
.then((sync) => {
|
|
||||||
dispatch(setFriendRequestCount(sync.friendRequestCount));
|
|
||||||
})
|
|
||||||
.catch(() => {});
|
|
||||||
}, [dispatch]);
|
|
||||||
|
|
||||||
const fetchFriendRequests = useCallback(async () => {
|
const fetchFriendRequests = useCallback(async () => {
|
||||||
return window.electron
|
return window.electron
|
||||||
.getFriendRequests()
|
.getFriendRequests()
|
||||||
.then((friendRequests) => {
|
.then((friendRequests) => {
|
||||||
syncFriendRequests();
|
window.electron.syncFriendRequests();
|
||||||
dispatch(setFriendRequests(friendRequests));
|
dispatch(setFriendRequests(friendRequests));
|
||||||
})
|
})
|
||||||
.catch(() => {});
|
.catch(() => {});
|
||||||
}, [dispatch, syncFriendRequests]);
|
}, [dispatch]);
|
||||||
|
|
||||||
const showFriendsModal = useCallback(
|
const showFriendsModal = useCallback(
|
||||||
(initialTab: UserFriendModalTab, userId: string) => {
|
(initialTab: UserFriendModalTab, userId: string) => {
|
||||||
@@ -146,8 +135,8 @@ export function useUserDetails() {
|
|||||||
const unblockUser = (userId: string) => window.electron.unblockUser(userId);
|
const unblockUser = (userId: string) => window.electron.unblockUser(userId);
|
||||||
|
|
||||||
const hasActiveSubscription = useMemo(() => {
|
const hasActiveSubscription = useMemo(() => {
|
||||||
const expiresAt = userDetails?.subscription?.expiresAt;
|
const expiresAt = new Date(userDetails?.subscription?.expiresAt ?? 0);
|
||||||
return expiresAt && (isFuture(expiresAt) || isToday(expiresAt));
|
return expiresAt > new Date();
|
||||||
}, [userDetails]);
|
}, [userDetails]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -168,7 +157,6 @@ export function useUserDetails() {
|
|||||||
patchUser,
|
patchUser,
|
||||||
sendFriendRequest,
|
sendFriendRequest,
|
||||||
fetchFriendRequests,
|
fetchFriendRequests,
|
||||||
syncFriendRequests,
|
|
||||||
updateFriendRequestState,
|
updateFriendRequestState,
|
||||||
blockUser,
|
blockUser,
|
||||||
unblockUser,
|
unblockUser,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import "@fontsource/noto-sans/500.css";
|
|||||||
import "@fontsource/noto-sans/700.css";
|
import "@fontsource/noto-sans/700.css";
|
||||||
|
|
||||||
import "react-loading-skeleton/dist/skeleton.css";
|
import "react-loading-skeleton/dist/skeleton.css";
|
||||||
|
import "react-tooltip/dist/react-tooltip.css";
|
||||||
|
|
||||||
import { App } from "./app";
|
import { App } from "./app";
|
||||||
|
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ $logo-max-width: 200px;
|
|||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: globals.$spacing-unit / 2;
|
gap: calc(globals.$spacing-unit / 2);
|
||||||
color: globals.$body-color;
|
color: globals.$body-color;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
|
|||||||
@@ -203,9 +203,10 @@ export function CloudSyncModal({ visible, onClose }: CloudSyncModalProps) {
|
|||||||
<div className="cloud-sync-modal__artifact-info">
|
<div className="cloud-sync-modal__artifact-info">
|
||||||
<div className="cloud-sync-modal__artifact-header">
|
<div className="cloud-sync-modal__artifact-header">
|
||||||
<h3>
|
<h3>
|
||||||
{t("backup_from", {
|
{artifact.label ??
|
||||||
date: format(artifact.createdAt, "dd/MM/yyyy"),
|
t("backup_from", {
|
||||||
})}
|
date: format(artifact.createdAt, "dd/MM/yyyy"),
|
||||||
|
})}
|
||||||
</h3>
|
</h3>
|
||||||
<small>{formatBytes(artifact.artifactLengthInBytes)}</small>
|
<small>{formatBytes(artifact.artifactLengthInBytes)}</small>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -23,6 +23,20 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__cloud-sync-label {
|
||||||
|
display: flex;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__cloud-sync-hydra-cloud {
|
||||||
|
background: linear-gradient(270deg, #16b195 50%, #3e62c0 100%);
|
||||||
|
color: #fff;
|
||||||
|
padding: 0 globals.$spacing-unit;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: globals.$small-font-size;
|
||||||
|
}
|
||||||
|
|
||||||
&__row {
|
&__row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: globals.$spacing-unit;
|
gap: globals.$spacing-unit;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useContext, useRef, useState } from "react";
|
import { useContext, useRef, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { Button, Modal, TextField } from "@renderer/components";
|
import { Button, CheckboxField, Modal, TextField } from "@renderer/components";
|
||||||
import type { LibraryGame } from "@types";
|
import type { LibraryGame } from "@types";
|
||||||
import { gameDetailsContext } from "@renderer/context";
|
import { gameDetailsContext } from "@renderer/context";
|
||||||
import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal";
|
import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal";
|
||||||
@@ -34,12 +34,17 @@ export function GameOptionsModal({
|
|||||||
achievements,
|
achievements,
|
||||||
} = useContext(gameDetailsContext);
|
} = useContext(gameDetailsContext);
|
||||||
|
|
||||||
|
const { hasActiveSubscription } = useUserDetails();
|
||||||
|
|
||||||
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
||||||
const [showRemoveGameModal, setShowRemoveGameModal] = useState(false);
|
const [showRemoveGameModal, setShowRemoveGameModal] = useState(false);
|
||||||
const [launchOptions, setLaunchOptions] = useState(game.launchOptions ?? "");
|
const [launchOptions, setLaunchOptions] = useState(game.launchOptions ?? "");
|
||||||
const [showResetAchievementsModal, setShowResetAchievementsModal] =
|
const [showResetAchievementsModal, setShowResetAchievementsModal] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [isDeletingAchievements, setIsDeletingAchievements] = useState(false);
|
const [isDeletingAchievements, setIsDeletingAchievements] = useState(false);
|
||||||
|
const [automaticCloudSync, setAutomaticCloudSync] = useState(
|
||||||
|
game.automaticCloudSync ?? false
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
removeGameInstaller,
|
removeGameInstaller,
|
||||||
@@ -183,6 +188,20 @@ export function GameOptionsModal({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleToggleAutomaticCloudSync = async (
|
||||||
|
event: React.ChangeEvent<HTMLInputElement>
|
||||||
|
) => {
|
||||||
|
setAutomaticCloudSync(event.target.checked);
|
||||||
|
|
||||||
|
await window.electron.toggleAutomaticCloudSync(
|
||||||
|
game.shop,
|
||||||
|
game.objectId,
|
||||||
|
event.target.checked
|
||||||
|
);
|
||||||
|
|
||||||
|
updateGame();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DeleteGameModal
|
<DeleteGameModal
|
||||||
@@ -266,6 +285,20 @@ export function GameOptionsModal({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<CheckboxField
|
||||||
|
label={
|
||||||
|
<div className="game-options-modal__cloud-sync-label">
|
||||||
|
{t("enable_automatic_cloud_sync")}
|
||||||
|
<span className="game-options-modal__cloud-sync-hydra-cloud">
|
||||||
|
Hydra Cloud
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
checked={automaticCloudSync}
|
||||||
|
disabled={!hasActiveSubscription || !game.executablePath}
|
||||||
|
onChange={handleToggleAutomaticCloudSync}
|
||||||
|
/>
|
||||||
|
|
||||||
{shouldShowWinePrefixConfiguration && (
|
{shouldShowWinePrefixConfiguration && (
|
||||||
<div className="game-options-modal__wine-prefix">
|
<div className="game-options-modal__wine-prefix">
|
||||||
<div className="game-options-modal__header">
|
<div className="game-options-modal__header">
|
||||||
|
|||||||
@@ -27,6 +27,11 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__badges {
|
||||||
|
display: flex;
|
||||||
|
gap: calc(globals.$spacing-unit / 2);
|
||||||
|
}
|
||||||
|
|
||||||
&__user-information {
|
&__user-information {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: calc(globals.$spacing-unit * 7) calc(globals.$spacing-unit * 3);
|
padding: calc(globals.$spacing-unit * 7) calc(globals.$spacing-unit * 3);
|
||||||
@@ -82,12 +87,6 @@
|
|||||||
text-shadow: 0 0 5px rgb(0 0 0 / 40%);
|
text-shadow: 0 0 5px rgb(0 0 0 / 40%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&__display-name-badges-container {
|
|
||||||
display: flex;
|
|
||||||
gap: globals.$spacing-unit;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__current-game {
|
&__current-game {
|
||||||
&-wrapper {
|
&-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import type { FriendRequestAction } from "@types";
|
|||||||
import { EditProfileModal } from "../edit-profile-modal/edit-profile-modal";
|
import { EditProfileModal } from "../edit-profile-modal/edit-profile-modal";
|
||||||
import Skeleton from "react-loading-skeleton";
|
import Skeleton from "react-loading-skeleton";
|
||||||
import { UploadBackgroundImageButton } from "../upload-background-image-button/upload-background-image-button";
|
import { UploadBackgroundImageButton } from "../upload-background-image-button/upload-background-image-button";
|
||||||
|
import { Tooltip } from "react-tooltip";
|
||||||
import "./profile-hero.scss";
|
import "./profile-hero.scss";
|
||||||
import { UserBadges } from "./user-badges";
|
|
||||||
|
|
||||||
type FriendAction =
|
type FriendAction =
|
||||||
| FriendRequestAction
|
| FriendRequestAction
|
||||||
@@ -35,8 +35,14 @@ export function ProfileHero() {
|
|||||||
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
|
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
|
||||||
const [isPerformingAction, setIsPerformingAction] = useState(false);
|
const [isPerformingAction, setIsPerformingAction] = useState(false);
|
||||||
|
|
||||||
const { isMe, getUserProfile, userProfile, heroBackground, backgroundImage } =
|
const {
|
||||||
useContext(userProfileContext);
|
isMe,
|
||||||
|
badges,
|
||||||
|
getUserProfile,
|
||||||
|
userProfile,
|
||||||
|
heroBackground,
|
||||||
|
backgroundImage,
|
||||||
|
} = useContext(userProfileContext);
|
||||||
const {
|
const {
|
||||||
signOut,
|
signOut,
|
||||||
updateFriendRequestState,
|
updateFriendRequestState,
|
||||||
@@ -261,14 +267,6 @@ export function ProfileHero() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{/* <ConfirmationModal
|
|
||||||
visible
|
|
||||||
title={t("sign_out_modal_title")}
|
|
||||||
descriptionText={t("sign_out_modal_text")}
|
|
||||||
confirmButtonLabel={t("sign_out")}
|
|
||||||
cancelButtonLabel={t("cancel")}
|
|
||||||
/> */}
|
|
||||||
|
|
||||||
<EditProfileModal
|
<EditProfileModal
|
||||||
visible={showEditProfileModal}
|
visible={showEditProfileModal}
|
||||||
onClose={() => setShowEditProfileModal(false)}
|
onClose={() => setShowEditProfileModal(false)}
|
||||||
@@ -312,7 +310,29 @@ export function ProfileHero() {
|
|||||||
<h2 className="profile-hero__display-name">
|
<h2 className="profile-hero__display-name">
|
||||||
{userProfile?.displayName}
|
{userProfile?.displayName}
|
||||||
</h2>
|
</h2>
|
||||||
<UserBadges />
|
|
||||||
|
<div className="profile-hero__badges">
|
||||||
|
{userProfile.badges.map((badgeName) => {
|
||||||
|
const badge = badges.find((b) => b.name === badgeName);
|
||||||
|
|
||||||
|
if (!badge) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<img
|
||||||
|
key={badge.name}
|
||||||
|
src={badge.badge.url}
|
||||||
|
alt={badge.name}
|
||||||
|
width={24}
|
||||||
|
height={24}
|
||||||
|
data-tooltip-place="top"
|
||||||
|
data-tooltip-content={badge.description}
|
||||||
|
data-tooltip-id="badge-name"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
|
||||||
|
<Tooltip id="badge-name" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<Skeleton width={150} height={28} />
|
<Skeleton width={150} height={28} />
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
import BadgeThemeCreator from "@renderer/assets/icons/badge-theme-creator.svg?react";
|
|
||||||
import "./profile-hero.scss";
|
|
||||||
import { useContext } from "react";
|
|
||||||
import { userProfileContext } from "@renderer/context";
|
|
||||||
import { UserBadge } from "@types";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
export function UserBadges() {
|
|
||||||
const { t } = useTranslation("badge");
|
|
||||||
const { userProfile } = useContext(userProfileContext);
|
|
||||||
|
|
||||||
if (!userProfile?.badges?.length) return null;
|
|
||||||
|
|
||||||
const getBadgeIcon = (badge: UserBadge) => {
|
|
||||||
if (badge === "THEME_CREATOR") {
|
|
||||||
return <BadgeThemeCreator width={24} height={24} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="profile-hero__display-name-badges-container">
|
|
||||||
{userProfile.badges.map((badge) => {
|
|
||||||
const badgeIcon = getBadgeIcon(badge);
|
|
||||||
|
|
||||||
if (!badgeIcon) return null;
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={`badge__${badge.toLowerCase()}`}
|
|
||||||
key={badge}
|
|
||||||
title={t(`badge_description_${badge.toLowerCase()}`)}
|
|
||||||
>
|
|
||||||
{badgeIcon}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -20,6 +20,7 @@ export function SettingsBehavior() {
|
|||||||
runAtStartup: false,
|
runAtStartup: false,
|
||||||
startMinimized: false,
|
startMinimized: false,
|
||||||
disableNsfwAlert: false,
|
disableNsfwAlert: false,
|
||||||
|
enableAutoInstall: false,
|
||||||
seedAfterDownloadComplete: false,
|
seedAfterDownloadComplete: false,
|
||||||
showHiddenAchievementsDescription: false,
|
showHiddenAchievementsDescription: false,
|
||||||
});
|
});
|
||||||
@@ -34,6 +35,7 @@ export function SettingsBehavior() {
|
|||||||
runAtStartup: userPreferences.runAtStartup ?? false,
|
runAtStartup: userPreferences.runAtStartup ?? false,
|
||||||
startMinimized: userPreferences.startMinimized ?? false,
|
startMinimized: userPreferences.startMinimized ?? false,
|
||||||
disableNsfwAlert: userPreferences.disableNsfwAlert ?? false,
|
disableNsfwAlert: userPreferences.disableNsfwAlert ?? false,
|
||||||
|
enableAutoInstall: userPreferences.enableAutoInstall ?? false,
|
||||||
seedAfterDownloadComplete:
|
seedAfterDownloadComplete:
|
||||||
userPreferences.seedAfterDownloadComplete ?? false,
|
userPreferences.seedAfterDownloadComplete ?? false,
|
||||||
showHiddenAchievementsDescription:
|
showHiddenAchievementsDescription:
|
||||||
@@ -99,6 +101,16 @@ export function SettingsBehavior() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{window.electron.platform === "linux" && (
|
||||||
|
<CheckboxField
|
||||||
|
label={t("enable_auto_install")}
|
||||||
|
checked={form.enableAutoInstall}
|
||||||
|
onChange={() =>
|
||||||
|
handleChange({ enableAutoInstall: !form.enableAutoInstall })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
label={t("disable_nsfw_alert")}
|
label={t("disable_nsfw_alert")}
|
||||||
checked={form.disableNsfwAlert}
|
checked={form.disableNsfwAlert}
|
||||||
|
|||||||
@@ -53,4 +53,9 @@
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__buttons-container {
|
||||||
|
display: flex;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,20 @@
|
|||||||
import { useContext, useEffect, useState } from "react";
|
import { useContext, useEffect, useState } from "react";
|
||||||
|
|
||||||
import { TextField, Button, Badge } from "@renderer/components";
|
import {
|
||||||
|
TextField,
|
||||||
|
Button,
|
||||||
|
Badge,
|
||||||
|
ConfirmationModal,
|
||||||
|
} from "@renderer/components";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import type { DownloadSource } from "@types";
|
import type { DownloadSource } from "@types";
|
||||||
import { NoEntryIcon, PlusCircleIcon, SyncIcon } from "@primer/octicons-react";
|
import {
|
||||||
|
NoEntryIcon,
|
||||||
|
PlusCircleIcon,
|
||||||
|
SyncIcon,
|
||||||
|
TrashIcon,
|
||||||
|
} from "@primer/octicons-react";
|
||||||
import { AddDownloadSourceModal } from "./add-download-source-modal";
|
import { AddDownloadSourceModal } from "./add-download-source-modal";
|
||||||
import { useAppDispatch, useRepacks, useToast } from "@renderer/hooks";
|
import { useAppDispatch, useRepacks, useToast } from "@renderer/hooks";
|
||||||
import { DownloadSourceStatus } from "@shared";
|
import { DownloadSourceStatus } from "@shared";
|
||||||
@@ -16,6 +26,10 @@ import { setFilters, clearFilters } from "@renderer/features";
|
|||||||
import "./settings-download-sources.scss";
|
import "./settings-download-sources.scss";
|
||||||
|
|
||||||
export function SettingsDownloadSources() {
|
export function SettingsDownloadSources() {
|
||||||
|
const [
|
||||||
|
showConfirmationDeleteAllSourcesModal,
|
||||||
|
setShowConfirmationDeleteAllSourcesModal,
|
||||||
|
] = useState(false);
|
||||||
const [showAddDownloadSourceModal, setShowAddDownloadSourceModal] =
|
const [showAddDownloadSourceModal, setShowAddDownloadSourceModal] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const [downloadSources, setDownloadSources] = useState<DownloadSource[]>([]);
|
const [downloadSources, setDownloadSources] = useState<DownloadSource[]>([]);
|
||||||
@@ -23,6 +37,7 @@ export function SettingsDownloadSources() {
|
|||||||
useState(false);
|
useState(false);
|
||||||
const [isRemovingDownloadSource, setIsRemovingDownloadSource] =
|
const [isRemovingDownloadSource, setIsRemovingDownloadSource] =
|
||||||
useState(false);
|
useState(false);
|
||||||
|
const [isFetchingSources, setIsFetchingSources] = useState(true);
|
||||||
|
|
||||||
const { sourceUrl, clearSourceUrl } = useContext(settingsContext);
|
const { sourceUrl, clearSourceUrl } = useContext(settingsContext);
|
||||||
|
|
||||||
@@ -41,6 +56,9 @@ export function SettingsDownloadSources() {
|
|||||||
.sortBy("createdAt")
|
.sortBy("createdAt")
|
||||||
.then((sources) => {
|
.then((sources) => {
|
||||||
setDownloadSources(sources.reverse());
|
setDownloadSources(sources.reverse());
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsFetchingSources(false);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,6 +86,25 @@ export function SettingsDownloadSources() {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleRemoveAllDownloadSources = () => {
|
||||||
|
setIsRemovingDownloadSource(true);
|
||||||
|
|
||||||
|
const id = crypto.randomUUID();
|
||||||
|
const channel = new BroadcastChannel(`download_sources:delete_all:${id}`);
|
||||||
|
|
||||||
|
downloadSourcesWorker.postMessage(["DELETE_ALL_DOWNLOAD_SOURCES", id]);
|
||||||
|
|
||||||
|
channel.onmessage = () => {
|
||||||
|
showSuccessToast(t("removed_download_sources"));
|
||||||
|
|
||||||
|
getDownloadSources();
|
||||||
|
setIsRemovingDownloadSource(false);
|
||||||
|
setShowConfirmationDeleteAllSourcesModal(false);
|
||||||
|
channel.close();
|
||||||
|
updateRepacks();
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const handleAddDownloadSource = async () => {
|
const handleAddDownloadSource = async () => {
|
||||||
await getDownloadSources();
|
await getDownloadSources();
|
||||||
showSuccessToast(t("added_download_source"));
|
showSuccessToast(t("added_download_source"));
|
||||||
@@ -115,6 +152,17 @@ export function SettingsDownloadSources() {
|
|||||||
onClose={handleModalClose}
|
onClose={handleModalClose}
|
||||||
onAddDownloadSource={handleAddDownloadSource}
|
onAddDownloadSource={handleAddDownloadSource}
|
||||||
/>
|
/>
|
||||||
|
<ConfirmationModal
|
||||||
|
cancelButtonLabel={t("cancel_button_confirmation_delete_all_sources")}
|
||||||
|
confirmButtonLabel={t("confirm_button_confirmation_delete_all_sources")}
|
||||||
|
descriptionText={t("description_confirmation_delete_all_sources")}
|
||||||
|
clickOutsideToClose={false}
|
||||||
|
onConfirm={handleRemoveAllDownloadSources}
|
||||||
|
visible={showConfirmationDeleteAllSourcesModal}
|
||||||
|
title={t("title_confirmation_delete_all_sources")}
|
||||||
|
onClose={() => setShowConfirmationDeleteAllSourcesModal(false)}
|
||||||
|
buttonsIsDisabled={isRemovingDownloadSource}
|
||||||
|
/>
|
||||||
|
|
||||||
<p>{t("download_sources_description")}</p>
|
<p>{t("download_sources_description")}</p>
|
||||||
|
|
||||||
@@ -125,7 +173,8 @@ export function SettingsDownloadSources() {
|
|||||||
disabled={
|
disabled={
|
||||||
!downloadSources.length ||
|
!downloadSources.length ||
|
||||||
isSyncingDownloadSources ||
|
isSyncingDownloadSources ||
|
||||||
isRemovingDownloadSource
|
isRemovingDownloadSource ||
|
||||||
|
isFetchingSources
|
||||||
}
|
}
|
||||||
onClick={syncDownloadSources}
|
onClick={syncDownloadSources}
|
||||||
>
|
>
|
||||||
@@ -133,15 +182,36 @@ export function SettingsDownloadSources() {
|
|||||||
{t("sync_download_sources")}
|
{t("sync_download_sources")}
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<Button
|
<div className="settings-download-sources__buttons-container">
|
||||||
type="button"
|
<Button
|
||||||
theme="outline"
|
type="button"
|
||||||
onClick={() => setShowAddDownloadSourceModal(true)}
|
theme="danger"
|
||||||
disabled={isSyncingDownloadSources}
|
onClick={() => setShowConfirmationDeleteAllSourcesModal(true)}
|
||||||
>
|
disabled={
|
||||||
<PlusCircleIcon />
|
isRemovingDownloadSource ||
|
||||||
{t("add_download_source")}
|
isSyncingDownloadSources ||
|
||||||
</Button>
|
!downloadSources.length ||
|
||||||
|
isFetchingSources
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<TrashIcon />
|
||||||
|
{t("button_delete_all_sources")}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
theme="outline"
|
||||||
|
onClick={() => setShowAddDownloadSourceModal(true)}
|
||||||
|
disabled={
|
||||||
|
isSyncingDownloadSources ||
|
||||||
|
isFetchingSources ||
|
||||||
|
isRemovingDownloadSource
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<PlusCircleIcon />
|
||||||
|
{t("add_download_source")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<ul className="settings-download-sources__list">
|
<ul className="settings-download-sources__list">
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export function SettingsGeneral() {
|
|||||||
downloadNotificationsEnabled: false,
|
downloadNotificationsEnabled: false,
|
||||||
repackUpdatesNotificationsEnabled: false,
|
repackUpdatesNotificationsEnabled: false,
|
||||||
achievementNotificationsEnabled: false,
|
achievementNotificationsEnabled: false,
|
||||||
|
friendRequestNotificationsEnabled: false,
|
||||||
language: "",
|
language: "",
|
||||||
|
|
||||||
customStyles: window.localStorage.getItem("customStyles") || "",
|
customStyles: window.localStorage.getItem("customStyles") || "",
|
||||||
@@ -82,6 +83,8 @@ export function SettingsGeneral() {
|
|||||||
userPreferences.repackUpdatesNotificationsEnabled ?? false,
|
userPreferences.repackUpdatesNotificationsEnabled ?? false,
|
||||||
achievementNotificationsEnabled:
|
achievementNotificationsEnabled:
|
||||||
userPreferences.achievementNotificationsEnabled ?? false,
|
userPreferences.achievementNotificationsEnabled ?? false,
|
||||||
|
friendRequestNotificationsEnabled:
|
||||||
|
userPreferences.friendRequestNotificationsEnabled ?? false,
|
||||||
language: language ?? "en",
|
language: language ?? "en",
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -171,6 +174,17 @@ export function SettingsGeneral() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<CheckboxField
|
||||||
|
label={t("enable_friend_request_notifications")}
|
||||||
|
checked={form.friendRequestNotificationsEnabled}
|
||||||
|
onChange={() =>
|
||||||
|
handleChange({
|
||||||
|
friendRequestNotificationsEnabled:
|
||||||
|
!form.friendRequestNotificationsEnabled,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,12 +7,22 @@
|
|||||||
gap: globals.$spacing-unit;
|
gap: globals.$spacing-unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__description {
|
|
||||||
margin-bottom: calc(globals.$spacing-unit * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__submit-button {
|
&__submit-button {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__description-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
margin-bottom: calc(globals.$spacing-unit * 2);
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__create-account {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,14 @@ import "./settings-real-debrid.scss";
|
|||||||
import { useAppSelector, useToast } from "@renderer/hooks";
|
import { useAppSelector, useToast } from "@renderer/hooks";
|
||||||
|
|
||||||
import { settingsContext } from "@renderer/context";
|
import { settingsContext } from "@renderer/context";
|
||||||
|
import { LinkExternalIcon } from "@primer/octicons-react";
|
||||||
|
|
||||||
|
const realDebridReferralId = import.meta.env
|
||||||
|
.RENDERER_VITE_REAL_DEBRID_REFERRAL_ID;
|
||||||
|
|
||||||
|
const REAL_DEBRID_URL = realDebridReferralId
|
||||||
|
? `https://real-debrid.com/?id=${realDebridReferralId}`
|
||||||
|
: "https://real-debrid.com";
|
||||||
const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken";
|
const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken";
|
||||||
|
|
||||||
export function SettingsRealDebrid() {
|
export function SettingsRealDebrid() {
|
||||||
@@ -74,24 +81,43 @@ export function SettingsRealDebrid() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const toggleRealDebrid = () => {
|
||||||
|
const updatedValue = !form.useRealDebrid;
|
||||||
|
|
||||||
|
setForm((prev) => ({
|
||||||
|
...prev,
|
||||||
|
useRealDebrid: updatedValue,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!updatedValue) {
|
||||||
|
updateUserPreferences({
|
||||||
|
realDebridApiToken: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const isButtonDisabled =
|
const isButtonDisabled =
|
||||||
(form.useRealDebrid && !form.realDebridApiToken) || isLoading;
|
(form.useRealDebrid && !form.realDebridApiToken) || isLoading;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="settings-real-debrid__form" onSubmit={handleFormSubmit}>
|
<form className="settings-real-debrid__form" onSubmit={handleFormSubmit}>
|
||||||
<p className="settings-real-debrid__description">
|
<div className="settings-real-debrid__description-container">
|
||||||
{t("real_debrid_description")}
|
<p className="settings-real-debrid__description">
|
||||||
</p>
|
{t("real_debrid_description")}
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
to={REAL_DEBRID_URL}
|
||||||
|
className="settings-real-debrid__create-account"
|
||||||
|
>
|
||||||
|
<LinkExternalIcon />
|
||||||
|
{t("create_real_debrid_account")}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
label={t("enable_real_debrid")}
|
label={t("enable_real_debrid")}
|
||||||
checked={form.useRealDebrid}
|
checked={form.useRealDebrid}
|
||||||
onChange={() => {
|
onChange={toggleRealDebrid}
|
||||||
setForm((prev) => ({
|
|
||||||
...prev,
|
|
||||||
useRealDebrid: !form.useRealDebrid,
|
|
||||||
}));
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{form.useRealDebrid && (
|
{form.useRealDebrid && (
|
||||||
|
|||||||
@@ -7,12 +7,22 @@
|
|||||||
gap: globals.$spacing-unit;
|
gap: globals.$spacing-unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__description {
|
|
||||||
margin-bottom: calc(globals.$spacing-unit * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
&__submit-button {
|
&__submit-button {
|
||||||
align-self: flex-end;
|
align-self: flex-end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__description-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
margin-bottom: calc(globals.$spacing-unit * 2);
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__create-account {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: globals.$spacing-unit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,13 @@ import "./settings-torbox.scss";
|
|||||||
import { useAppSelector, useToast } from "@renderer/hooks";
|
import { useAppSelector, useToast } from "@renderer/hooks";
|
||||||
|
|
||||||
import { settingsContext } from "@renderer/context";
|
import { settingsContext } from "@renderer/context";
|
||||||
|
import { LinkExternalIcon } from "@primer/octicons-react";
|
||||||
|
|
||||||
|
const torBoxReferralCode = import.meta.env.RENDERER_VITE_TORBOX_REFERRAL_CODE;
|
||||||
|
|
||||||
|
const TORBOX_URL = torBoxReferralCode
|
||||||
|
? `https://torbox.app/subscription?referral=${torBoxReferralCode}`
|
||||||
|
: "https://torbox.app";
|
||||||
const TORBOX_API_TOKEN_URL = "https://torbox.app/settings";
|
const TORBOX_API_TOKEN_URL = "https://torbox.app/settings";
|
||||||
|
|
||||||
export function SettingsTorbox() {
|
export function SettingsTorbox() {
|
||||||
@@ -69,19 +75,37 @@ export function SettingsTorbox() {
|
|||||||
const isButtonDisabled =
|
const isButtonDisabled =
|
||||||
(form.useTorBox && !form.torBoxApiToken) || isLoading;
|
(form.useTorBox && !form.torBoxApiToken) || isLoading;
|
||||||
|
|
||||||
|
const toggleTorBox = () => {
|
||||||
|
const updatedValue = !form.useTorBox;
|
||||||
|
|
||||||
|
setForm((prev) => ({
|
||||||
|
...prev,
|
||||||
|
useTorBox: updatedValue,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!updatedValue) {
|
||||||
|
updateUserPreferences({
|
||||||
|
torBoxApiToken: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form className="settings-torbox__form" onSubmit={handleFormSubmit}>
|
<form className="settings-torbox__form" onSubmit={handleFormSubmit}>
|
||||||
<p className="settings-torbox__description">{t("torbox_description")}</p>
|
<div className="settings-torbox__description-container">
|
||||||
|
<p className="settings-torbox__description">
|
||||||
|
{t("torbox_description")}
|
||||||
|
</p>
|
||||||
|
<Link to={TORBOX_URL} className="settings-torbox__create-account">
|
||||||
|
<LinkExternalIcon />
|
||||||
|
{t("create_torbox_account")}
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
|
||||||
<CheckboxField
|
<CheckboxField
|
||||||
label={t("enable_torbox")}
|
label={t("enable_torbox")}
|
||||||
checked={form.useTorBox}
|
checked={form.useTorBox}
|
||||||
onChange={() =>
|
onChange={toggleTorBox}
|
||||||
setForm((prev) => ({
|
|
||||||
...prev,
|
|
||||||
useTorBox: !form.useTorBox,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{form.useTorBox && (
|
{form.useTorBox && (
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@use "../../scss/globals.scss";
|
@use "../../scss/globals.scss";
|
||||||
|
@use "sass:color";
|
||||||
|
|
||||||
.theme-editor {
|
.theme-editor {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -58,7 +59,10 @@
|
|||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|
||||||
.active {
|
.active {
|
||||||
background-color: darken(globals.$dark-background-color, 2%);
|
background-color: color.adjust(
|
||||||
|
globals.$dark-background-color,
|
||||||
|
$lightness: -2%
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/renderer/src/vite-env.d.ts
vendored
2
src/renderer/src/vite-env.d.ts
vendored
@@ -4,6 +4,8 @@
|
|||||||
interface ImportMetaEnv {
|
interface ImportMetaEnv {
|
||||||
readonly RENDERER_VITE_EXTERNAL_RESOURCES_URL: string;
|
readonly RENDERER_VITE_EXTERNAL_RESOURCES_URL: string;
|
||||||
readonly RENDERER_VITE_SENTRY_DSN: string;
|
readonly RENDERER_VITE_SENTRY_DSN: string;
|
||||||
|
readonly RENDERER_VITE_REAL_DEBRID_REFERRAL_ID: string;
|
||||||
|
readonly RENDERER_VITE_TORBOX_REFERRAL_CODE: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ImportMeta {
|
interface ImportMeta {
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ type Payload =
|
|||||||
| ["IMPORT_DOWNLOAD_SOURCE", string]
|
| ["IMPORT_DOWNLOAD_SOURCE", string]
|
||||||
| ["DELETE_DOWNLOAD_SOURCE", number]
|
| ["DELETE_DOWNLOAD_SOURCE", number]
|
||||||
| ["VALIDATE_DOWNLOAD_SOURCE", string]
|
| ["VALIDATE_DOWNLOAD_SOURCE", string]
|
||||||
| ["SYNC_DOWNLOAD_SOURCES", string];
|
| ["SYNC_DOWNLOAD_SOURCES", string]
|
||||||
|
| ["DELETE_ALL_DOWNLOAD_SOURCES", string];
|
||||||
|
|
||||||
export type SteamGamesByLetter = Record<string, { id: string; name: string }[]>;
|
export type SteamGamesByLetter = Record<string, { id: string; name: string }[]>;
|
||||||
|
|
||||||
@@ -114,6 +115,13 @@ const deleteDownloadSource = async (id: number) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const deleteAllDowloadSources = async () => {
|
||||||
|
await db.transaction("rw", repacksTable, downloadSourcesTable, async () => {
|
||||||
|
await repacksTable.clear();
|
||||||
|
await downloadSourcesTable.clear();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
self.onmessage = async (event: MessageEvent<Payload>) => {
|
self.onmessage = async (event: MessageEvent<Payload>) => {
|
||||||
const [type, data] = event.data;
|
const [type, data] = event.data;
|
||||||
|
|
||||||
@@ -132,6 +140,14 @@ self.onmessage = async (event: MessageEvent<Payload>) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type === "DELETE_ALL_DOWNLOAD_SOURCES") {
|
||||||
|
await deleteAllDowloadSources();
|
||||||
|
|
||||||
|
const channel = new BroadcastChannel(`download_sources:delete_all:${data}`);
|
||||||
|
|
||||||
|
channel.postMessage(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (type === "DELETE_DOWNLOAD_SOURCE") {
|
if (type === "DELETE_DOWNLOAD_SOURCE") {
|
||||||
await deleteDownloadSource(data);
|
await deleteDownloadSource(data);
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,13 @@ export interface UserProfileCurrentGame extends Omit<GameRunning, "objectId"> {
|
|||||||
|
|
||||||
export type ProfileVisibility = "PUBLIC" | "PRIVATE" | "FRIENDS";
|
export type ProfileVisibility = "PUBLIC" | "PRIVATE" | "FRIENDS";
|
||||||
|
|
||||||
export type UserBadge = "THEME_CREATOR";
|
export interface Badge {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
badge: {
|
||||||
|
url: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface UserDetails {
|
export interface UserDetails {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -166,7 +172,7 @@ export interface UserProfile {
|
|||||||
quirks: {
|
quirks: {
|
||||||
backupsPerGameLimit: number;
|
backupsPerGameLimit: number;
|
||||||
};
|
};
|
||||||
badges: UserBadge[];
|
badges: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateProfileRequest {
|
export interface UpdateProfileRequest {
|
||||||
@@ -247,6 +253,7 @@ export interface GameArtifact {
|
|||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
hostname: string;
|
hostname: string;
|
||||||
downloadCount: number;
|
downloadCount: number;
|
||||||
|
label?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ComparedAchievements {
|
export interface ComparedAchievements {
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ export interface Game {
|
|||||||
executablePath?: string | null;
|
executablePath?: string | null;
|
||||||
launchOptions?: string | null;
|
launchOptions?: string | null;
|
||||||
favorite?: boolean;
|
favorite?: boolean;
|
||||||
|
automaticCloudSync?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Download {
|
export interface Download {
|
||||||
@@ -75,9 +76,19 @@ export interface UserPreferences {
|
|||||||
runAtStartup?: boolean;
|
runAtStartup?: boolean;
|
||||||
startMinimized?: boolean;
|
startMinimized?: boolean;
|
||||||
disableNsfwAlert?: boolean;
|
disableNsfwAlert?: boolean;
|
||||||
|
enableAutoInstall?: boolean;
|
||||||
seedAfterDownloadComplete?: boolean;
|
seedAfterDownloadComplete?: boolean;
|
||||||
showHiddenAchievementsDescription?: boolean;
|
showHiddenAchievementsDescription?: boolean;
|
||||||
downloadNotificationsEnabled?: boolean;
|
downloadNotificationsEnabled?: boolean;
|
||||||
repackUpdatesNotificationsEnabled?: boolean;
|
repackUpdatesNotificationsEnabled?: boolean;
|
||||||
achievementNotificationsEnabled?: boolean;
|
achievementNotificationsEnabled?: boolean;
|
||||||
|
friendRequestNotificationsEnabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScreenState {
|
||||||
|
x?: number;
|
||||||
|
y?: number;
|
||||||
|
height: number;
|
||||||
|
width: number;
|
||||||
|
isMaximized: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
61
yarn.lock
61
yarn.lock
@@ -1663,6 +1663,14 @@
|
|||||||
"@floating-ui/core" "^1.6.0"
|
"@floating-ui/core" "^1.6.0"
|
||||||
"@floating-ui/utils" "^0.2.8"
|
"@floating-ui/utils" "^0.2.8"
|
||||||
|
|
||||||
|
"@floating-ui/dom@^1.6.1":
|
||||||
|
version "1.6.13"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.13.tgz#a8a938532aea27a95121ec16e667a7cbe8c59e34"
|
||||||
|
integrity sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/core" "^1.6.0"
|
||||||
|
"@floating-ui/utils" "^0.2.9"
|
||||||
|
|
||||||
"@floating-ui/react-dom@^2.0.0":
|
"@floating-ui/react-dom@^2.0.0":
|
||||||
version "2.1.2"
|
version "2.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz#a1349bbf6a0e5cb5ded55d023766f20a4d439a31"
|
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz#a1349bbf6a0e5cb5ded55d023766f20a4d439a31"
|
||||||
@@ -1675,6 +1683,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62"
|
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62"
|
||||||
integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==
|
integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==
|
||||||
|
|
||||||
|
"@floating-ui/utils@^0.2.9":
|
||||||
|
version "0.2.9"
|
||||||
|
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.9.tgz#50dea3616bc8191fb8e112283b49eaff03e78429"
|
||||||
|
integrity sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==
|
||||||
|
|
||||||
"@fontsource/noto-sans@^5.1.0":
|
"@fontsource/noto-sans@^5.1.0":
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/@fontsource/noto-sans/-/noto-sans-5.1.0.tgz#54a5edd1b2b8c8e17bec6a85d4ee3a53b4b89c1f"
|
resolved "https://registry.yarnpkg.com/@fontsource/noto-sans/-/noto-sans-5.1.0.tgz#54a5edd1b2b8c8e17bec6a85d4ee3a53b4b89c1f"
|
||||||
@@ -3559,6 +3572,11 @@ agent-base@^7.0.2, agent-base@^7.1.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
debug "^4.3.4"
|
debug "^4.3.4"
|
||||||
|
|
||||||
|
agent-base@^7.1.3:
|
||||||
|
version "7.1.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1"
|
||||||
|
integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==
|
||||||
|
|
||||||
agentkeepalive@^4.2.1:
|
agentkeepalive@^4.2.1:
|
||||||
version "4.5.0"
|
version "4.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
|
resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923"
|
||||||
@@ -3895,6 +3913,13 @@ axe-core@^4.10.0:
|
|||||||
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59"
|
resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.10.0.tgz#d9e56ab0147278272739a000880196cdfe113b59"
|
||||||
integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==
|
integrity sha512-Mr2ZakwQ7XUAjp7pAwQWRhhK8mQQ6JAaNWSjmjxil0R8BPioMtQsTLOolGYkji1rcL++3dCqZA3zWqpT+9Ew6g==
|
||||||
|
|
||||||
|
axios-cookiejar-support@^5.0.5:
|
||||||
|
version "5.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios-cookiejar-support/-/axios-cookiejar-support-5.0.5.tgz#6030e17b438a64e3967a5ca190ee4202481c0ecf"
|
||||||
|
integrity sha512-jJG+p7JnOYxkVrYkCDKBrLqUmcpwHZTNQrEcIEKr5qe7YVTyPAD9nCsi1cO5LDmQpQApfS430czO+oceI3g/3g==
|
||||||
|
dependencies:
|
||||||
|
http-cookie-agent "^6.0.8"
|
||||||
|
|
||||||
axios@^1.7.9:
|
axios@^1.7.9:
|
||||||
version "1.7.9"
|
version "1.7.9"
|
||||||
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a"
|
resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.9.tgz#d7d071380c132a24accda1b2cfc1535b79ec650a"
|
||||||
@@ -4279,7 +4304,7 @@ classic-level@^2.0.0:
|
|||||||
napi-macros "^2.2.2"
|
napi-macros "^2.2.2"
|
||||||
node-gyp-build "^4.3.0"
|
node-gyp-build "^4.3.0"
|
||||||
|
|
||||||
classnames@^2.2.1, classnames@^2.2.6, classnames@^2.5.1:
|
classnames@^2.2.1, classnames@^2.2.6, classnames@^2.3.0, classnames@^2.5.1:
|
||||||
version "2.5.1"
|
version "2.5.1"
|
||||||
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
|
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
|
||||||
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
|
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
|
||||||
@@ -6134,6 +6159,13 @@ http-cache-semantics@^4.0.0, http-cache-semantics@^4.1.0:
|
|||||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||||
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
|
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
|
||||||
|
|
||||||
|
http-cookie-agent@^6.0.8:
|
||||||
|
version "6.0.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/http-cookie-agent/-/http-cookie-agent-6.0.8.tgz#f2635638f4172c7de0c482396ea7313e9731a62b"
|
||||||
|
integrity sha512-qnYh3yLSr2jBsTYkw11elq+T361uKAJaZ2dR4cfYZChw1dt9uL5t3zSUwehoqqVb4oldk1BpkXKm2oat8zV+oA==
|
||||||
|
dependencies:
|
||||||
|
agent-base "^7.1.3"
|
||||||
|
|
||||||
http-proxy-agent@^5.0.0:
|
http-proxy-agent@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
|
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
|
||||||
@@ -8093,6 +8125,14 @@ react-style-singleton@^2.2.1:
|
|||||||
invariant "^2.2.4"
|
invariant "^2.2.4"
|
||||||
tslib "^2.0.0"
|
tslib "^2.0.0"
|
||||||
|
|
||||||
|
react-tooltip@^5.28.0:
|
||||||
|
version "5.28.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-5.28.0.tgz#c7b5343ab2d740a428494a3d8315515af1f26f46"
|
||||||
|
integrity sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==
|
||||||
|
dependencies:
|
||||||
|
"@floating-ui/dom" "^1.6.1"
|
||||||
|
classnames "^2.3.0"
|
||||||
|
|
||||||
react@^18.2.0:
|
react@^18.2.0:
|
||||||
version "18.3.1"
|
version "18.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
|
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"
|
||||||
@@ -9127,6 +9167,18 @@ tinyexec@^0.3.0:
|
|||||||
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98"
|
resolved "https://registry.yarnpkg.com/tinyexec/-/tinyexec-0.3.1.tgz#0ab0daf93b43e2c211212396bdb836b468c97c98"
|
||||||
integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==
|
integrity sha512-WiCJLEECkO18gwqIp6+hJg0//p23HXp4S+gGtAKu3mI2F2/sXC4FvHvXvB0zJVVaTPhx1/tOwdbRsa1sOBIKqQ==
|
||||||
|
|
||||||
|
tldts-core@^6.1.78:
|
||||||
|
version "6.1.78"
|
||||||
|
resolved "https://registry.yarnpkg.com/tldts-core/-/tldts-core-6.1.78.tgz#47b477d9742870daa01dbd5ff9a598a48379728c"
|
||||||
|
integrity sha512-jS0svNsB99jR6AJBmfmEWuKIgz91Haya91Z43PATaeHJ24BkMoNRb/jlaD37VYjb0mYf6gRL/HOnvS1zEnYBiw==
|
||||||
|
|
||||||
|
tldts@^6.1.32:
|
||||||
|
version "6.1.78"
|
||||||
|
resolved "https://registry.yarnpkg.com/tldts/-/tldts-6.1.78.tgz#ee94576653a60d421ff94162c4e9060f2e62467b"
|
||||||
|
integrity sha512-fSgYrW0ITH0SR/CqKMXIruYIPpNu5aDgUp22UhYoSrnUQwc7SBqifEBFNce7AAcygUPBo6a/gbtcguWdmko4RQ==
|
||||||
|
dependencies:
|
||||||
|
tldts-core "^6.1.78"
|
||||||
|
|
||||||
tmp-promise@^3.0.2:
|
tmp-promise@^3.0.2:
|
||||||
version "3.0.3"
|
version "3.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7"
|
resolved "https://registry.yarnpkg.com/tmp-promise/-/tmp-promise-3.0.3.tgz#60a1a1cc98c988674fcbfd23b6e3367bdeac4ce7"
|
||||||
@@ -9174,6 +9226,13 @@ tough-cookie@^4.1.4:
|
|||||||
universalify "^0.2.0"
|
universalify "^0.2.0"
|
||||||
url-parse "^1.5.3"
|
url-parse "^1.5.3"
|
||||||
|
|
||||||
|
tough-cookie@^5.1.1:
|
||||||
|
version "5.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-5.1.1.tgz#4641c1fdbf024927e29c5532edb7b6e5377ea1f2"
|
||||||
|
integrity sha512-Ek7HndSVkp10hmHP9V4qZO1u+pn1RU5sI0Fw+jCU3lyvuMZcgqsNgc6CmJJZyByK4Vm/qotGRJlfgAX8q+4JiA==
|
||||||
|
dependencies:
|
||||||
|
tldts "^6.1.32"
|
||||||
|
|
||||||
tr46@^5.0.0:
|
tr46@^5.0.0:
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec"
|
resolved "https://registry.yarnpkg.com/tr46/-/tr46-5.0.0.tgz#3b46d583613ec7283020d79019f1335723801cec"
|
||||||
|
|||||||
Reference in New Issue
Block a user