mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
feat: updated profile game sorting
This commit is contained in:
parent
385db5c936
commit
953b7b3ad8
28 changed files with 996 additions and 278 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,4 +1,5 @@
|
||||||
.vscode/
|
.vscode/
|
||||||
|
.vs/
|
||||||
node_modules/
|
node_modules/
|
||||||
__pycache__
|
__pycache__
|
||||||
dist
|
dist
|
||||||
|
|
|
@ -125,6 +125,10 @@ cd hydra
|
||||||
yarn
|
yarn
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### <a name="install-openssl-11"></a> Instale OpenSSL 1.1
|
||||||
|
|
||||||
|
[OpenSSL 1.1](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe) é exigido pelo libtorrent em ambientes Windows.
|
||||||
|
|
||||||
### <a name="install-python-39"></a> Instale Python 3.9
|
### <a name="install-python-39"></a> Instale Python 3.9
|
||||||
|
|
||||||
Certifique-se de ter o Python 3.9 instalado em sua máquina. Você pode baixá-lo e instalá-lo em [python.org](https://www.python.org/downloads/release/python-3913/).
|
Certifique-se de ter o Python 3.9 instalado em sua máquina. Você pode baixá-lo e instalá-lo em [python.org](https://www.python.org/downloads/release/python-3913/).
|
||||||
|
|
|
@ -280,7 +280,23 @@
|
||||||
"launch_minimized": "Launch Hydra minimized",
|
"launch_minimized": "Launch Hydra minimized",
|
||||||
"disable_nsfw_alert": "Disable NSFW alert",
|
"disable_nsfw_alert": "Disable NSFW alert",
|
||||||
"seed_after_download_complete": "Seed after download complete",
|
"seed_after_download_complete": "Seed after download complete",
|
||||||
"show_hidden_achievement_description": "Show hidden achievements description before unlocking them"
|
"show_hidden_achievement_description": "Show hidden achievements description before unlocking them",
|
||||||
|
"account": "Account",
|
||||||
|
"no_users_blocked": "You have no blocked users",
|
||||||
|
"subscription_active_until": "Your Hydra Cloud is active until {{date}}",
|
||||||
|
"manage_subscription": "Manage subscription",
|
||||||
|
"update_email": "Update email",
|
||||||
|
"update_password": "Update password",
|
||||||
|
"current_email": "Current email:",
|
||||||
|
"no_email_account": "You have not set an email yet",
|
||||||
|
"account_data_updated_successfully": "Account data updated successfully",
|
||||||
|
"renew_subscription": "Renew Hydra Cloud",
|
||||||
|
"subscription_expired_at": "Your subscription expired at {{date}}",
|
||||||
|
"no_subscription": "Enjoy Hydra in the best possible way",
|
||||||
|
"become_subscriber": "Be Hydra Cloud",
|
||||||
|
"subscription_renew_cancelled": "Automatic renewal is disabled",
|
||||||
|
"subscription_renews_on": "Your subscription renews on {{date}}",
|
||||||
|
"bill_sent_until": "Your next bill will be sent until this day"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Download complete",
|
"download_complete": "Download complete",
|
||||||
|
|
|
@ -268,7 +268,23 @@
|
||||||
"launch_minimized": "Iniciar o Hydra minimizado",
|
"launch_minimized": "Iniciar o Hydra minimizado",
|
||||||
"disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado",
|
"disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado",
|
||||||
"seed_after_download_complete": "Semear após a conclusão do download",
|
"seed_after_download_complete": "Semear após a conclusão do download",
|
||||||
"show_hidden_achievement_description": "Mostrar descrição de conquistas ocultas antes de debloqueá-las"
|
"show_hidden_achievement_description": "Mostrar descrição de conquistas ocultas antes de debloqueá-las",
|
||||||
|
"account": "Conta",
|
||||||
|
"no_users_blocked": "Você não bloqueou nenhum usuário",
|
||||||
|
"subscription_active_until": "Sua assinatura Hydra Cloud ficará ativa até {{date}}",
|
||||||
|
"manage_subscription": "Gerenciar assinatura",
|
||||||
|
"update_email": "Atualizar email",
|
||||||
|
"update_password": "Atualizar senha",
|
||||||
|
"current_email": "Email atual:",
|
||||||
|
"no_email_account": "Você ainda não adicionou um email a sua conta",
|
||||||
|
"account_data_updated_successfully": "Dados da conta atualizados com sucesso",
|
||||||
|
"renew_subscription": "Renovar Hydra Cloud",
|
||||||
|
"subscription_expired_at": "Sua assinatura expirou em {{date}}",
|
||||||
|
"no_subscription": "Aproveite o Hydra da melhor forma possível",
|
||||||
|
"become_subscriber": "Seja Hydra Cloud",
|
||||||
|
"subscription_renew_cancelled": "A renovação automática está desativada",
|
||||||
|
"subscription_renews_on": "Sua assinatura renova dia {{date}}",
|
||||||
|
"bill_sent_until": "Sua próxima cobrança será enviada até esse dia"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Download concluído",
|
"download_complete": "Download concluído",
|
||||||
|
@ -397,7 +413,7 @@
|
||||||
"new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos",
|
"new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos",
|
||||||
"achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas",
|
"achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas",
|
||||||
"achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}",
|
"achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}",
|
||||||
"hidden_achievement_tooltip": "Está é uma conquista oculta",
|
"hidden_achievement_tooltip": "Esta é uma conquista oculta",
|
||||||
"achievement_earn_points": "Ganhe {{points}} pontos com essa conquista",
|
"achievement_earn_points": "Ganhe {{points}} pontos com essa conquista",
|
||||||
"earned_points": "Pontos ganhos:",
|
"earned_points": "Pontos ganhos:",
|
||||||
"available_points": "Pontos disponíveis:",
|
"available_points": "Pontos disponíveis:",
|
||||||
|
|
|
@ -167,6 +167,9 @@
|
||||||
"loading_save_preview": "Поиск сохранений…",
|
"loading_save_preview": "Поиск сохранений…",
|
||||||
"wine_prefix": "Префикс Wine",
|
"wine_prefix": "Префикс Wine",
|
||||||
"wine_prefix_description": "Префикс Wine, используемый для запуска этой игры",
|
"wine_prefix_description": "Префикс Wine, используемый для запуска этой игры",
|
||||||
|
"launch_options": "Параметры запуска",
|
||||||
|
"launch_options_description": "Опытные пользователи могут внести изменения в параметры запуска",
|
||||||
|
"launch_options_placeholder": "Параметр не указан ",
|
||||||
"no_download_option_info": "Информация недоступна",
|
"no_download_option_info": "Информация недоступна",
|
||||||
"backup_deletion_failed": "Не удалось удалить резервную копию",
|
"backup_deletion_failed": "Не удалось удалить резервную копию",
|
||||||
"max_number_of_artifacts_reached": "Достигнуто максимальное количество резервных копий для этой игры",
|
"max_number_of_artifacts_reached": "Достигнуто максимальное количество резервных копий для этой игры",
|
||||||
|
@ -175,7 +178,11 @@
|
||||||
"select_folder": "Выбрать папку",
|
"select_folder": "Выбрать папку",
|
||||||
"backup_from": "Резервная копия от {{date}}",
|
"backup_from": "Резервная копия от {{date}}",
|
||||||
"custom_backup_location_set": "Установлено настраиваемое местоположение резервной копии",
|
"custom_backup_location_set": "Установлено настраиваемое местоположение резервной копии",
|
||||||
"no_directory_selected": "Не выбран каталог"
|
"no_directory_selected": "Не выбран каталог",
|
||||||
|
"no_write_permission": "Невозможно загрузить в эту директорию. Нажмите здесь, чтобы узнать больше.",
|
||||||
|
"reset_achievements_title": "Вы уверены?",
|
||||||
|
"reset_achievements_success": "Достижения успешно сброшены",
|
||||||
|
"reset_achievements_error": "Не удалось сбросить достижения"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Активировать Hydra",
|
"title": "Активировать Hydra",
|
||||||
|
@ -271,7 +278,23 @@
|
||||||
"source_already_exists": "Этот источник уже добавлен",
|
"source_already_exists": "Этот источник уже добавлен",
|
||||||
"user_unblocked": "Пользователь разблокирован",
|
"user_unblocked": "Пользователь разблокирован",
|
||||||
"seed_after_download_complete": "Раздавать после завершения загрузки",
|
"seed_after_download_complete": "Раздавать после завершения загрузки",
|
||||||
"show_hidden_achievement_description": "Показывать описание скрытых достижений перед их получением"
|
"show_hidden_achievement_description": "Показывать описание скрытых достижений перед их получением",
|
||||||
|
"account": "Аккаунт",
|
||||||
|
"no_users_blocked": "У вас нет заблокированных пользователей",
|
||||||
|
"subscription_active_until": "Ваша подписка на Hydra Cloud активна до {{date}}",
|
||||||
|
"manage_subscription": "Управлять подпиской",
|
||||||
|
"update_email": "Обновить электронную почту",
|
||||||
|
"update_password": "Обновить пароль",
|
||||||
|
"current_email": "Текущий email:",
|
||||||
|
"no_email_account": "Вы еще не установили электронную почту",
|
||||||
|
"account_data_updated_successfully": "Данные учетной записи успешно обновлены",
|
||||||
|
"renew_subscription": "Обновить подписку Hydra Cloud",
|
||||||
|
"subscription_expired_at": "Срок действия вашей подписки истек в {{date}}",
|
||||||
|
"no_subscription": "Наслаждайтесь Hydra по максимуму",
|
||||||
|
"become_subscriber": "Станьте обладателем Hydra Cloud",
|
||||||
|
"subscription_renew_cancelled": "Автоматическое продление отключено",
|
||||||
|
"subscription_renews_on": "Ваша подписка продлевается на {{date}}",
|
||||||
|
"bill_sent_until": "Ваш следующий счет будет отправлен до этого дня"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "Загрузка завершена",
|
"download_complete": "Загрузка завершена",
|
||||||
|
|
|
@ -1,131 +1,423 @@
|
||||||
{
|
{
|
||||||
"language_name": "Türkçe",
|
"language_name": "Türkçe",
|
||||||
|
"app": {
|
||||||
|
"successfully_signed_in": "Başarıyla giriş yapıldı"
|
||||||
|
},
|
||||||
"home": {
|
"home": {
|
||||||
"featured": "Öne çıkan",
|
"featured": "Öne Çıkanlar",
|
||||||
"surprise_me": "Şaşırt beni",
|
"surprise_me": "Beni Şaşırt",
|
||||||
"no_results": "Sonuç bulunamadı"
|
"no_results": "Sonuç bulunamadı",
|
||||||
|
"start_typing": "Aramak için yazmaya başlayın...",
|
||||||
|
"hot": "Şu anda popüler",
|
||||||
|
"weekly": "📅 Haftanın en iyi oyunları",
|
||||||
|
"achievements": "🏆 Tamamlanacak oyunlar"
|
||||||
},
|
},
|
||||||
"sidebar": {
|
"sidebar": {
|
||||||
"catalogue": "Katalog",
|
"catalogue": "Katalog",
|
||||||
"downloads": "İndirmeler",
|
"downloads": "İndirilenler",
|
||||||
"settings": "Ayarlar",
|
"settings": "Ayarlar",
|
||||||
"my_library": "Kütüphane",
|
"my_library": "Kütüphanem",
|
||||||
"downloading_metadata": "{{title}} (Metadata indiriliyor…)",
|
"downloading_metadata": "{{title}} (Meta verileri indiriliyor…)",
|
||||||
"paused": "{{title}} (Duraklatıldı)",
|
"paused": "{{title}} (Durduruldu)",
|
||||||
"downloading": "{{title}} ({{percentage}} - İndiriliyor…)",
|
"downloading": "{{title}} ({{percentage}} - İndiriliyor…)",
|
||||||
"filter": "Kütüphaneyi filtrele",
|
"filter": "Kütüphaneyi filtrele",
|
||||||
"home": "Ana menü"
|
"home": "Ana Sayfa",
|
||||||
|
"queued": "{{title}} (Sırada)",
|
||||||
|
"game_has_no_executable": "Oyun için bir çalıştırılabilir dosya seçilmedi",
|
||||||
|
"sign_in": "Giriş yap",
|
||||||
|
"friends": "Arkadaşlar",
|
||||||
|
"need_help": "Yardıma mı ihtiyacınız var?"
|
||||||
},
|
},
|
||||||
"header": {
|
"header": {
|
||||||
"search": "Ara",
|
"search": "Oyunları ara",
|
||||||
"home": "Ana menü",
|
"home": "Ana Sayfa",
|
||||||
"catalogue": "Katalog",
|
"catalogue": "Katalog",
|
||||||
"downloads": "İndirmeler",
|
"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_download": "Sürüm {{version}} mevcut. İndirmek için buraya tıklayın."
|
||||||
},
|
},
|
||||||
"bottom_panel": {
|
"bottom_panel": {
|
||||||
"no_downloads_in_progress": "İndirilen bir şey yok",
|
"no_downloads_in_progress": "Devam eden indirme yok",
|
||||||
"downloading_metadata": "{{title}} metadatası indiriliyor…",
|
"downloading_metadata": "{{title}} meta verileri indiriliyor…",
|
||||||
"downloading": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Bitiş {{eta}} - {{speed}}"
|
"downloading": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Tamamlama: {{eta}} - Hız: {{speed}}",
|
||||||
|
"calculating_eta": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Kalan süre hesaplanıyor…",
|
||||||
|
"checking_files": "{{title}} dosyaları kontrol ediliyor… ({{percentage}} tamamlandı)"
|
||||||
},
|
},
|
||||||
"catalogue": {
|
"catalogue": {
|
||||||
"next_page": "Sonraki sayfa",
|
"search": "Filtrele…",
|
||||||
"previous_page": "Önceki sayfa"
|
"developers": "Geliştiriciler",
|
||||||
|
"genres": "Türler",
|
||||||
|
"tags": "Etiketler",
|
||||||
|
"publishers": "Yayıncılar",
|
||||||
|
"download_sources": "İndirme kaynakları",
|
||||||
|
"result_count": "{{resultCount}} sonuç",
|
||||||
|
"filter_count": "{{filterCount}} mevcut",
|
||||||
|
"clear_filters": "{{filterCount}} seçili filtreyi temizle"
|
||||||
},
|
},
|
||||||
"game_details": {
|
"game_details": {
|
||||||
"open_download_options": "İndirme seçeneklerini aç",
|
"open_download_options": "İndirme seçeneklerini aç",
|
||||||
"download_options_zero": "İndirme seçeneği yok",
|
"download_options_zero": "İndirme seçeneği yok",
|
||||||
"download_options_one": "{{count}} indirme seçeneği",
|
"download_options_one": "{{count}} indirme seçeneği",
|
||||||
"download_options_other": "{{count}} indirme seçeneği",
|
"download_options_other": "{{count}} indirme seçeneği",
|
||||||
"updated_at": "{{updated_at}} güncellendi",
|
"updated_at": "{{updated_at}} tarihinde güncellendi",
|
||||||
"install": "İndir",
|
"install": "Yükle",
|
||||||
"resume": "Devam et",
|
"resume": "Devam et",
|
||||||
"pause": "Duraklat",
|
"pause": "Durdur",
|
||||||
"cancel": "İptal et",
|
"cancel": "İptal et",
|
||||||
"remove": "Sil",
|
"remove": "Kaldır",
|
||||||
"space_left_on_disk": "Diskte {{space}} yer kaldı",
|
"space_left_on_disk": "Diskte {{space}} boş alan kaldı",
|
||||||
"eta": "Bitiş {{eta}}",
|
"eta": "{{eta}} tahmini bitiş",
|
||||||
"downloading_metadata": "Metadata indiriliyor…",
|
"calculating_eta": "Kalan süre hesaplanıyor…",
|
||||||
"filter": "Repackleri filtrele",
|
"downloading_metadata": "Meta veriler indiriliyor…",
|
||||||
|
"filter": "Paketleri filtrele",
|
||||||
"requirements": "Sistem gereksinimleri",
|
"requirements": "Sistem gereksinimleri",
|
||||||
"minimum": "Minimum",
|
"minimum": "Minimum",
|
||||||
"recommended": "Önerilen",
|
"recommended": "Önerilen",
|
||||||
"release_date": "{{date}} tarihinde çıktı",
|
"paused": "Durduruldu",
|
||||||
"publisher": "{{publisher}} tarihinde yayınlandı",
|
"release_date": "{{date}} tarihinde yayımlandı",
|
||||||
"hours": "saatler",
|
"publisher": "{{publisher}} tarafından yayımlandı",
|
||||||
"minutes": "dakikalar",
|
"hours": "saat",
|
||||||
|
"minutes": "dakika",
|
||||||
"amount_hours": "{{amount}} saat",
|
"amount_hours": "{{amount}} saat",
|
||||||
"amount_minutes": "{{amount}} dakika",
|
"amount_minutes": "{{amount}} dakika",
|
||||||
"accuracy": "%{{accuracy}} doğruluk",
|
"accuracy": "{{accuracy}}% doğruluk",
|
||||||
"add_to_library": "Kütüphaneye ekle",
|
"add_to_library": "Kütüphaneye ekle",
|
||||||
"remove_from_library": "Kütüphaneden kaldır",
|
"remove_from_library": "Kütüphaneden kaldır",
|
||||||
"no_downloads": "İndirme yok",
|
"no_downloads": "İndirilebilir içerik yok",
|
||||||
"play_time": "{{amount}} oynandı",
|
"play_time": "{{amount}} süre oynandı",
|
||||||
"last_time_played": "Son oynanan {{period}}",
|
"last_time_played": "Son oynama {{period}} önce",
|
||||||
"not_played_yet": "Bu {{title}} hiç oynanmadı",
|
"not_played_yet": "{{title}} henüz oynanmadı",
|
||||||
"next_suggestion": "Sıradaki öneri",
|
"next_suggestion": "Sonraki öneri",
|
||||||
"play": "Oyna",
|
"play": "Oyna",
|
||||||
"deleting": "Installer siliniyor…",
|
"deleting": "Yükleyici siliniyor…",
|
||||||
"close": "Kapat",
|
"close": "Kapat",
|
||||||
"playing_now": "Şimdi oynanıyor",
|
"playing_now": "Şu anda oynanıyor",
|
||||||
"change": "Değiştir",
|
"change": "Değiştir",
|
||||||
"repacks_modal_description": "İndirmek istediğiiniz repacki seçin",
|
"repacks_modal_description": "İndirmek istediğiniz paketi seçin",
|
||||||
"select_folder_hint": "Varsayılan klasörü değiştirmek için ulaşmanız gereken ayar",
|
"select_folder_hint": "Varsayılan klasörü değiştirmek için <0>Ayarlar</0> bölümüne gidin",
|
||||||
"download_now": "Şimdi"
|
"download_now": "Şimdi indir",
|
||||||
|
"no_shop_details": "Mağaza bilgileri alınamadı.",
|
||||||
|
"download_options": "İndirme seçenekleri",
|
||||||
|
"download_path": "İndirme yolu",
|
||||||
|
"previous_screenshot": "Önceki ekran görüntüsü",
|
||||||
|
"next_screenshot": "Sonraki ekran görüntüsü",
|
||||||
|
"screenshot": "{{number}} ekran görüntüsü",
|
||||||
|
"open_screenshot": "{{number}} ekran görüntüsünü aç",
|
||||||
|
"download_settings": "İndirme ayarları",
|
||||||
|
"downloader": "İndirici",
|
||||||
|
"select_executable": "Seç",
|
||||||
|
"no_executable_selected": "Hiçbir çalıştırılabilir dosya seçilmedi",
|
||||||
|
"open_folder": "Klasörü aç",
|
||||||
|
"open_download_location": "İndirilen dosyaları gör",
|
||||||
|
"create_shortcut": "Masaüstü kısayolu oluştur",
|
||||||
|
"clear": "Temizle",
|
||||||
|
"remove_files": "Dosyaları kaldır",
|
||||||
|
"remove_from_library_title": "Emin misiniz?",
|
||||||
|
"remove_from_library_description": "Bu işlem {{game}} oyununu kütüphanenizden kaldıracaktır",
|
||||||
|
"options": "Seçenekler",
|
||||||
|
"executable_section_title": "Çalıştırılabilir dosya",
|
||||||
|
"executable_section_description": "\"Oyna\" tıklandığında çalıştırılacak dosyanın yolu",
|
||||||
|
"downloads_secion_title": "İndirmeler",
|
||||||
|
"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_description": "Bu oyunu kütüphanenizden veya Hydra tarafından indirilen dosyaları kaldırın",
|
||||||
|
"download_in_progress": "İndirme devam ediyor",
|
||||||
|
"download_paused": "İndirme durduruldu",
|
||||||
|
"last_downloaded_option": "Son indirilen seçenek",
|
||||||
|
"create_shortcut_success": "Kısayol başarıyla oluşturuldu",
|
||||||
|
"create_shortcut_error": "Kısayol oluşturulurken hata oluştu",
|
||||||
|
"nsfw_content_title": "Bu oyun uygunsuz içerik içeriyor",
|
||||||
|
"nsfw_content_description": "{{title}} her yaş için uygun olmayabilecek içeriklere sahiptir. Devam etmek istediğinizden emin misiniz?",
|
||||||
|
"allow_nsfw_content": "Devam et",
|
||||||
|
"refuse_nsfw_content": "Geri dön",
|
||||||
|
"stats": "İstatistikler",
|
||||||
|
"download_count": "İndirme sayısı",
|
||||||
|
"player_count": "Aktif oyuncular",
|
||||||
|
"download_error": "Bu indirme seçeneği mevcut değil",
|
||||||
|
"download": "İndir",
|
||||||
|
"executable_path_in_use": "\"{{game}}\" tarafından kullanılan çalıştırılabilir dosya",
|
||||||
|
"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.",
|
||||||
|
"achievements": "Başarılar",
|
||||||
|
"achievements_count": "Başarılar {{unlockedCount}}/{{achievementsCount}}",
|
||||||
|
"cloud_save": "Bulut kaydı",
|
||||||
|
"cloud_save_description": "İlerlemenizi buluta kaydedin ve herhangi bir cihazda oynamaya devam edin",
|
||||||
|
"backups": "Yedekler",
|
||||||
|
"install_backup": "Yükle",
|
||||||
|
"delete_backup": "Sil",
|
||||||
|
"create_backup": "Yeni yedek oluştur",
|
||||||
|
"last_backup_date": "{{date}} tarihindeki son yedek",
|
||||||
|
"no_backup_preview": "Bu oyun için kayıtlı oyun bulunamadı",
|
||||||
|
"restoring_backup": "Yedek geri yükleniyor ({{progress}} tamamlandı)…",
|
||||||
|
"uploading_backup": "Yedek yükleniyor…",
|
||||||
|
"no_backups": "Bu oyun için henüz bir yedek oluşturmadınız",
|
||||||
|
"backup_uploaded": "Yedek yüklendi",
|
||||||
|
"backup_deleted": "Yedek silindi",
|
||||||
|
"backup_restored": "Yedek geri yüklendi",
|
||||||
|
"see_all_achievements": "Tüm başarıları gör",
|
||||||
|
"sign_in_to_see_achievements": "Başarıları görmek için giriş yapın",
|
||||||
|
"mapping_method_automatic": "Otomatik",
|
||||||
|
"mapping_method_manual": "Manuel",
|
||||||
|
"mapping_method_label": "Eşleme yöntemi",
|
||||||
|
"files_automatically_mapped": "Dosyalar otomatik olarak eşlendi",
|
||||||
|
"no_backups_created": "Bu oyun için yedek oluşturulmadı",
|
||||||
|
"manage_files": "Dosyaları yönet",
|
||||||
|
"loading_save_preview": "Kayıtlı oyunlar aranıyor…",
|
||||||
|
"wine_prefix": "Wine Prefix",
|
||||||
|
"wine_prefix_description": "Bu oyunu çalıştırmak için kullanılan Wine Prefix",
|
||||||
|
"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_placeholder": "Belirtilen bir parametre yok",
|
||||||
|
"no_download_option_info": "Bilgi mevcut değil",
|
||||||
|
"backup_deletion_failed": "Yedek silinemedi",
|
||||||
|
"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",
|
||||||
|
"manage_files_description": "Hangi dosyaların yedeklenip geri yükleneceğini yönetin",
|
||||||
|
"select_folder": "Klasör seç",
|
||||||
|
"backup_from": "{{date}} tarihinden yedek",
|
||||||
|
"custom_backup_location_set": "Özel yedekleme konumu ayarlandı",
|
||||||
|
"no_directory_selected": "Bir dizin seçilmedi",
|
||||||
|
"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_description": "Bu işlem {{game}} için tüm başarıları sıfırlar",
|
||||||
|
"reset_achievements_title": "Emin misiniz?",
|
||||||
|
"reset_achievements_success": "Başarılar başarıyla sıfırlandı",
|
||||||
|
"reset_achievements_error": "Başarılar sıfırlanamadı"
|
||||||
},
|
},
|
||||||
"activation": {
|
"activation": {
|
||||||
"title": "Hydra'yı aktif et",
|
"title": "Hydra'yı Aktive Et",
|
||||||
"installation_id": "Kurulum ID'si:",
|
"installation_id": "Kurulum Kimliği:",
|
||||||
"enter_activation_code": "Aktifleştirme kodunuzu girin",
|
"enter_activation_code": "Aktivasyon kodunuzu girin",
|
||||||
"message": "Bunu nerede soracağınızı bilmiyorsanız, buna sahip olmamanız gerekiyor.",
|
"message": "Bunu nereden soracağınızı bilmiyorsanız, bu sizin için olmamalı.",
|
||||||
"activate": "Aktif et",
|
"activate": "Aktive Et",
|
||||||
"loading": "Yükleniyor…"
|
"loading": "Yükleniyor…"
|
||||||
},
|
},
|
||||||
"downloads": {
|
"downloads": {
|
||||||
"resume": "Devam et",
|
"resume": "Devam Et",
|
||||||
"pause": "Duraklat",
|
"pause": "Duraklat",
|
||||||
"eta": "Bitiş {{eta}}",
|
"eta": "Tamamlama {{eta}}",
|
||||||
"paused": "Duraklatıldı",
|
"paused": "Duraklatıldı",
|
||||||
"verifying": "Doğrulanıyor…",
|
"verifying": "Doğrulanıyor…",
|
||||||
"completed": "Tamamlandı",
|
"completed": "Tamamlandı",
|
||||||
"cancel": "İptal et",
|
"removed": "İndirilmedi",
|
||||||
"filter": "Yüklü oyunları filtrele",
|
"cancel": "İptal Et",
|
||||||
|
"filter": "İndirilen oyunları filtrele",
|
||||||
"remove": "Kaldır",
|
"remove": "Kaldır",
|
||||||
"downloading_metadata": "Metadata indiriliyor…",
|
"downloading_metadata": "Metadata indiriliyor…",
|
||||||
"deleting": "Installer siliniyor…",
|
"deleting": "Yükleyici siliniyor…",
|
||||||
"delete": "Installer'ı sil",
|
"delete": "Yükleyiciyi kaldır",
|
||||||
"delete_modal_title": "Emin misiniz?",
|
"delete_modal_title": "Emin misiniz?",
|
||||||
"delete_modal_description": "Bu bilgisayarınızdan tüm kurulum dosyalarını silecek",
|
"delete_modal_description": "Bu işlem, tüm kurulum dosyalarını bilgisayarınızdan kaldıracaktır",
|
||||||
"install": "Kur"
|
"install": "Kur",
|
||||||
|
"download_in_progress": "Devam ediyor",
|
||||||
|
"queued_downloads": "Sıradaki indirmeler",
|
||||||
|
"downloads_completed": "Tamamlananlar",
|
||||||
|
"queued": "Sırada",
|
||||||
|
"no_downloads_title": "Bomboş",
|
||||||
|
"no_downloads_description": "Henüz Hydra ile hiçbir şey indirmediniz, ancak başlamak için asla geç değil.",
|
||||||
|
"checking_files": "Dosyalar kontrol ediliyor…",
|
||||||
|
"seeding": "Paylaşılıyor",
|
||||||
|
"stop_seeding": "Paylaşımı durdur",
|
||||||
|
"resume_seeding": "Paylaşımı sürdür",
|
||||||
|
"options": "Yönet"
|
||||||
},
|
},
|
||||||
"settings": {
|
"settings": {
|
||||||
"downloads_path": "İndirme yolu",
|
"downloads_path": "İndirme yolu",
|
||||||
"change": "Güncelle",
|
"change": "Güncelle",
|
||||||
"notifications": "Bildirimler",
|
"notifications": "Bildirimler",
|
||||||
"enable_download_notifications": "Bir indirme bittiğinde",
|
"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ı",
|
||||||
|
"quit_app_instead_hiding": "Hydra'yı kapatırken gizlemeyin",
|
||||||
|
"launch_with_system": "Hydra'yı sistem başlatıldığında çalıştır",
|
||||||
|
"general": "Genel",
|
||||||
|
"behavior": "Davranış",
|
||||||
|
"download_sources": "İndirme kaynakları",
|
||||||
|
"language": "Dil",
|
||||||
|
"real_debrid_api_token": "API Anahtarı",
|
||||||
|
"enable_real_debrid": "Real-Debrid'i Etkinleştir",
|
||||||
|
"real_debrid_description": "Real-Debrid, yalnızca internet hızınızla sınırlı olarak hızlı dosya indirmenizi sağlayan sınırsız bir indirici.",
|
||||||
|
"real_debrid_invalid_token": "Geçersiz API anahtarı",
|
||||||
|
"real_debrid_api_token_hint": "API anahtarınızı <0>buradan</0> alabilirsiniz",
|
||||||
|
"real_debrid_free_account_error": "\"{{username}}\" hesabı ücretsiz bir hesaptır. Lütfen Real-Debrid abonesi olun",
|
||||||
|
"real_debrid_linked_message": "\"{{username}}\" hesabı bağlandı",
|
||||||
|
"save_changes": "Değişiklikleri Kaydet",
|
||||||
|
"changes_saved": "Değişiklikler başarıyla kaydedildi",
|
||||||
|
"download_sources_description": "Hydra, indirme bağlantılarını bu kaynaklardan alacak. Kaynak URL, indirme bağlantılarını içeren bir .json dosyasına doğrudan bir bağlantı olmalıdır.",
|
||||||
|
"validate_download_source": "Doğrula",
|
||||||
|
"remove_download_source": "Kaldır",
|
||||||
|
"add_download_source": "Kaynak ekle",
|
||||||
|
"download_count_zero": "İndirme seçeneği yok",
|
||||||
|
"download_count_one": "{{countFormatted}} indirme seçeneği",
|
||||||
|
"download_count_other": "{{countFormatted}} indirme seçeneği",
|
||||||
|
"download_source_url": "İndirme kaynağı URL'si",
|
||||||
|
"add_download_source_description": ".json dosyasının URL'sini girin",
|
||||||
|
"download_source_up_to_date": "Güncel",
|
||||||
|
"download_source_errored": "Hatalı",
|
||||||
|
"sync_download_sources": "Kaynakları senkronize et",
|
||||||
|
"removed_download_source": "İndirme kaynağı kaldırıldı",
|
||||||
|
"added_download_source": "İndirme kaynağı eklendi",
|
||||||
|
"download_sources_synced": "Tüm indirme kaynakları senkronize edildi",
|
||||||
|
"insert_valid_json_url": "Geçerli bir JSON URL'si girin",
|
||||||
|
"found_download_option_zero": "Hiçbir indirme seçeneği bulunamadı",
|
||||||
|
"found_download_option_one": "{{countFormatted}} indirme seçeneği bulundu",
|
||||||
|
"found_download_option_other": "{{countFormatted}} indirme seçeneği bulundu",
|
||||||
|
"import": "İçe aktar",
|
||||||
|
"public": "Herkese açık",
|
||||||
|
"private": "Gizli",
|
||||||
|
"friends_only": "Sadece arkadaşlar",
|
||||||
|
"privacy": "Gizlilik",
|
||||||
|
"profile_visibility": "Profil görünürlüğü",
|
||||||
|
"profile_visibility_description": "Profilinizi ve kütüphanenizi kimlerin görebileceğini seçin",
|
||||||
|
"required_field": "Bu alan gereklidir",
|
||||||
|
"source_already_exists": "Bu kaynak zaten eklenmiş",
|
||||||
|
"must_be_valid_url": "Kaynak geçerli bir URL olmalıdır",
|
||||||
|
"blocked_users": "Engellenen kullanıcılar",
|
||||||
|
"user_unblocked": "Kullanıcının engeli kaldırıldı",
|
||||||
|
"enable_achievement_notifications": "Bir başarı kilidi açıldığında",
|
||||||
|
"launch_minimized": "Hydra'yı küçültülmüş başlat",
|
||||||
|
"disable_nsfw_alert": "NSFW uyarısını devre dışı bırak",
|
||||||
|
"seed_after_download_complete": "İndirme tamamlandıktan sonra paylaş",
|
||||||
|
"show_hidden_achievement_description": "Gizli başarı açıklamalarını kilitlenmeden önce göster"
|
||||||
},
|
},
|
||||||
"notifications": {
|
"notifications": {
|
||||||
"download_complete": "İndirme tamamlandı",
|
"download_complete": "İndirme tamamlandı",
|
||||||
"game_ready_to_install": "{{title}} kuruluma hazır",
|
"game_ready_to_install": "{{title}} kurulmaya hazır",
|
||||||
"repack_list_updated": "Repack listesi güncellendi",
|
"repack_list_updated": "Repack listesi güncellendi",
|
||||||
"repack_count_one": "{{count}} yeni repack eklendi",
|
"repack_count_one": "{{count}} repack eklendi",
|
||||||
"repack_count_other": "{{count}} yeni repack eklendi"
|
"repack_count_other": "{{count}} repack eklendi",
|
||||||
|
"new_update_available": "Sürüm {{version}} mevcut",
|
||||||
|
"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_body": "{{achievement}} ve diğer {{count}} başarılar açıldı"
|
||||||
},
|
},
|
||||||
"system_tray": {
|
"system_tray": {
|
||||||
"open": "Hydra'yı aç",
|
"open": "Hydra'yı Aç",
|
||||||
"quit": "Çık"
|
"quit": "Çık"
|
||||||
},
|
},
|
||||||
"game_card": {
|
"game_card": {
|
||||||
"no_downloads": "İndirme mevcut değil"
|
"no_downloads": "İndirilebilir içerik bulunmuyor"
|
||||||
},
|
},
|
||||||
"binary_not_found_modal": {
|
"binary_not_found_modal": {
|
||||||
"title": "Programlar yüklü değil",
|
"title": "Programlar Yüklü Değil",
|
||||||
"description": "Sisteminizde Wine veya Lutris çalıştırılabiliri bulunamadı",
|
"description": "Wine veya Lutris çalıştırılabilir dosyaları sisteminizde bulunamadı",
|
||||||
"instructions": "Oyunları düzgün şekilde çalıştırmak için Linux distronuza bunlardan birini nasıl yükleyebileceğinize bakın"
|
"instructions": "Oyunun normal çalışabilmesi için bunlardan herhangi birini Linux dağıtımınıza uygun şekilde nasıl kuracağınızı kontrol edin"
|
||||||
},
|
},
|
||||||
"modal": {
|
"modal": {
|
||||||
"close": "Kapat tuşu"
|
"close": "Kapat düğmesi"
|
||||||
|
},
|
||||||
|
"forms": {
|
||||||
|
"toggle_password_visibility": "Şifre görünürlüğünü değiştir"
|
||||||
|
},
|
||||||
|
"user_profile": {
|
||||||
|
"amount_hours": "{{amount}} saat",
|
||||||
|
"amount_minutes": "{{amount}} dakika",
|
||||||
|
"last_time_played": "Son oynanma {{period}}",
|
||||||
|
"activity": "Son Etkinlik",
|
||||||
|
"library": "Kütüphane",
|
||||||
|
"total_play_time": "Toplam oynama süresi",
|
||||||
|
"no_recent_activity_title": "Hmmm… burada bir şey yok",
|
||||||
|
"no_recent_activity_description": "Son zamanlarda hiç oyun oynamamışsınız. Bunu değiştirmenin zamanı geldi!",
|
||||||
|
"display_name": "Görünen isim",
|
||||||
|
"saving": "Kaydediliyor",
|
||||||
|
"save": "Kaydet",
|
||||||
|
"edit_profile": "Profili Düzenle",
|
||||||
|
"saved_successfully": "Başarıyla kaydedildi",
|
||||||
|
"try_again": "Lütfen tekrar deneyin",
|
||||||
|
"sign_out_modal_title": "Emin misiniz?",
|
||||||
|
"cancel": "İptal",
|
||||||
|
"successfully_signed_out": "Başarıyla çıkış yapıldı",
|
||||||
|
"sign_out": "Çıkış yap",
|
||||||
|
"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?",
|
||||||
|
"add_friends": "Arkadaş Ekle",
|
||||||
|
"add": "Ekle",
|
||||||
|
"friend_code": "Arkadaş kodu",
|
||||||
|
"see_profile": "Profili gör",
|
||||||
|
"sending": "Gönderiliyor",
|
||||||
|
"friend_request_sent": "Arkadaşlık isteği gönderildi",
|
||||||
|
"friends": "Arkadaşlar",
|
||||||
|
"friends_list": "Arkadaş listesi",
|
||||||
|
"user_not_found": "Kullanıcı bulunamadı",
|
||||||
|
"block_user": "Kullanıcıyı engelle",
|
||||||
|
"add_friend": "Arkadaş ekle",
|
||||||
|
"request_sent": "İstek gönderildi",
|
||||||
|
"request_received": "İstek alındı",
|
||||||
|
"accept_request": "İsteği kabul et",
|
||||||
|
"ignore_request": "İsteği yok say",
|
||||||
|
"cancel_request": "İsteği iptal et",
|
||||||
|
"undo_friendship": "Arkadaşlığı sonlandır",
|
||||||
|
"request_accepted": "İstek kabul edildi",
|
||||||
|
"user_blocked_successfully": "Kullanıcı başarıyla engellendi",
|
||||||
|
"user_block_modal_text": "Bu işlem {{displayName}} adlı kullanıcıyı engelleyecek",
|
||||||
|
"blocked_users": "Engellenen kullanıcılar",
|
||||||
|
"unblock": "Engeli kaldır",
|
||||||
|
"no_friends_added": "Hiç arkadaş eklemediniz",
|
||||||
|
"pending": "Bekliyor",
|
||||||
|
"no_pending_invites": "Bekleyen davetiniz yok",
|
||||||
|
"no_blocked_users": "Engellenmiş kullanıcı yok",
|
||||||
|
"friend_code_copied": "Arkadaş kodu kopyalandı",
|
||||||
|
"undo_friendship_modal_text": "Bu işlem {{displayName}} ile arkadaşlığınızı sonlandıracak",
|
||||||
|
"privacy_hint": "Bunu kimin görebileceğini ayarlamak için <0>Ayarlar</0> bölümüne gidin",
|
||||||
|
"locked_profile": "Bu profil gizli",
|
||||||
|
"image_process_failure": "Görüntü işleme başarısız oldu",
|
||||||
|
"required_field": "Bu alan gerekli",
|
||||||
|
"displayname_min_length": "Görünen isim en az 3 karakter uzunluğunda olmalıdır",
|
||||||
|
"displayname_max_length": "Görünen isim en fazla 50 karakter uzunluğunda olabilir",
|
||||||
|
"report_profile": "Bu profili bildir",
|
||||||
|
"report_reason": "Bu profili neden bildiriyorsunuz?",
|
||||||
|
"report_description": "Ek bilgi",
|
||||||
|
"report_description_placeholder": "Ek bilgi",
|
||||||
|
"report": "Bildir",
|
||||||
|
"report_reason_hate": "Nefret söylemi",
|
||||||
|
"report_reason_sexual_content": "Cinsel içerik",
|
||||||
|
"report_reason_violence": "Şiddet",
|
||||||
|
"report_reason_spam": "Spam",
|
||||||
|
"report_reason_other": "Diğer",
|
||||||
|
"profile_reported": "Profil bildirildi",
|
||||||
|
"your_friend_code": "Arkadaş kodunuz:",
|
||||||
|
"upload_banner": "Afiş yükle",
|
||||||
|
"uploading_banner": "Afiş yükleniyor…",
|
||||||
|
"background_image_updated": "Arka plan görüntüsü güncellendi",
|
||||||
|
"stats": "İstatistikler",
|
||||||
|
"achievements": "Başarılar",
|
||||||
|
"games": "Oyunlar",
|
||||||
|
"top_percentile": "En üst {{percentile}}%",
|
||||||
|
"ranking_updated_weekly": "Sıralama haftalık olarak güncellenir",
|
||||||
|
"playing": "{{game}} oynanıyor",
|
||||||
|
"achievements_unlocked": "Başarılar açıldı",
|
||||||
|
"earned_points": "Kazanılan puanlar",
|
||||||
|
"show_achievements_on_profile": "Başarılarınızı profilinizde gösterin",
|
||||||
|
"show_points_on_profile": "Kazandığınız puanları profilinizde gösterin"
|
||||||
|
},
|
||||||
|
"achievement": {
|
||||||
|
"achievement_unlocked": "Başarı açıldı",
|
||||||
|
"user_achievements": "{{displayName}}'in Başarıları",
|
||||||
|
"your_achievements": "Başarılarınız",
|
||||||
|
"unlocked_at": "Açılma zamanı: {{date}}",
|
||||||
|
"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ı",
|
||||||
|
"achievement_progress": "{{unlockedCount}}/{{totalCount}} başarı",
|
||||||
|
"achievements_unlocked_for_game": "{{gameTitle}} oyunu için {{achievementCount}} yeni başarı açıldı",
|
||||||
|
"hidden_achievement_tooltip": "Bu gizli bir başarıdır",
|
||||||
|
"achievement_earn_points": "Bu başarı ile {{points}} puan kazanın",
|
||||||
|
"earned_points": "Kazanılan puanlar:",
|
||||||
|
"available_points": "Mevcut puanlar:",
|
||||||
|
"how_to_earn_achievements_points": "Başarı puanları nasıl kazanılır?"
|
||||||
|
},
|
||||||
|
"hydra_cloud": {
|
||||||
|
"subscription_tour_title": "Hydra Cloud Aboneliği",
|
||||||
|
"subscribe_now": "Şimdi abone olun",
|
||||||
|
"cloud_saving": "Bulut kaydetme",
|
||||||
|
"cloud_achievements": "Başarılarınızı buluta kaydedin",
|
||||||
|
"animated_profile_picture": "Animasyonlu profil resimleri",
|
||||||
|
"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",
|
||||||
|
"animated_profile_banner": "Animasyonlu profil afişi",
|
||||||
|
"hydra_cloud": "Hydra Cloud",
|
||||||
|
"hydra_cloud_feature_found": "Bir Hydra Cloud özelliği keşfettiniz!",
|
||||||
|
"learn_more": "Daha Fazla Bilgi Edinin"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,24 @@
|
||||||
|
import i18next from "i18next";
|
||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import { WindowManager } from "@main/services";
|
import { HydraApi, WindowManager } from "@main/services";
|
||||||
|
import { AuthPage } from "@shared";
|
||||||
|
|
||||||
const openAuthWindow = async (_event: Electron.IpcMainInvokeEvent) =>
|
const openAuthWindow = async (
|
||||||
WindowManager.openAuthWindow();
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
|
page: AuthPage
|
||||||
|
) => {
|
||||||
|
const searchParams = new URLSearchParams({
|
||||||
|
lng: i18next.language,
|
||||||
|
});
|
||||||
|
|
||||||
|
if ([AuthPage.UpdateEmail, AuthPage.UpdatePassword].includes(page)) {
|
||||||
|
const { accessToken } = await HydraApi.refreshToken().catch(() => {
|
||||||
|
return { accessToken: "" };
|
||||||
|
});
|
||||||
|
searchParams.set("token", accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowManager.openAuthWindow(page, searchParams);
|
||||||
|
};
|
||||||
|
|
||||||
registerEvent("openAuthWindow", openAuthWindow);
|
registerEvent("openAuthWindow", openAuthWindow);
|
||||||
|
|
|
@ -8,7 +8,7 @@ import {
|
||||||
} from "@main/repository";
|
} from "@main/repository";
|
||||||
import { publishDownloadCompleteNotification } from "../notifications";
|
import { publishDownloadCompleteNotification } from "../notifications";
|
||||||
import type { DownloadProgress } from "@types";
|
import type { DownloadProgress } from "@types";
|
||||||
import { GofileApi, QiwiApi } from "../hosters";
|
import { GofileApi, QiwiApi, DatanodesApi } from "../hosters";
|
||||||
import { PythonRPC } from "../python-rpc";
|
import { PythonRPC } from "../python-rpc";
|
||||||
import {
|
import {
|
||||||
LibtorrentPayload,
|
LibtorrentPayload,
|
||||||
|
@ -277,6 +277,16 @@ export class DownloadManager {
|
||||||
save_path: game.downloadPath!,
|
save_path: game.downloadPath!,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
case Downloader.Datanodes: {
|
||||||
|
const downloadUrl = await DatanodesApi.getDownloadUrl(game.uri!);
|
||||||
|
|
||||||
|
return {
|
||||||
|
action: "start",
|
||||||
|
game_id: game.id,
|
||||||
|
url: downloadUrl,
|
||||||
|
save_path: game.downloadPath!,
|
||||||
|
};
|
||||||
|
}
|
||||||
case Downloader.Torrent:
|
case Downloader.Torrent:
|
||||||
return {
|
return {
|
||||||
action: "start",
|
action: "start",
|
||||||
|
|
47
src/main/services/hosters/datanodes.ts
Normal file
47
src/main/services/hosters/datanodes.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
import axios, { AxiosResponse } from "axios";
|
||||||
|
|
||||||
|
export class DatanodesApi {
|
||||||
|
private static readonly session = axios.create({});
|
||||||
|
|
||||||
|
public static async getDownloadUrl(downloadUrl: string): Promise<string> {
|
||||||
|
const parsedUrl = new URL(downloadUrl);
|
||||||
|
const pathSegments = parsedUrl.pathname.split("/");
|
||||||
|
|
||||||
|
const fileCode = decodeURIComponent(pathSegments[1]);
|
||||||
|
const fileName = decodeURIComponent(pathSegments[pathSegments.length - 1]);
|
||||||
|
|
||||||
|
const payload = new URLSearchParams({
|
||||||
|
op: "download2",
|
||||||
|
id: fileCode,
|
||||||
|
rand: "",
|
||||||
|
referer: "https://datanodes.to/download",
|
||||||
|
method_free: "Free Download >>",
|
||||||
|
method_premium: "",
|
||||||
|
adblock_detected: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const response: AxiosResponse = await this.session.post(
|
||||||
|
"https://datanodes.to/download",
|
||||||
|
payload,
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
Cookie: `lang=english; file_name=${fileName}; file_code=${fileCode};`,
|
||||||
|
Host: "datanodes.to",
|
||||||
|
Origin: "https://datanodes.to",
|
||||||
|
Referer: "https://datanodes.to/download",
|
||||||
|
"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,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.status === 302) {
|
||||||
|
return response.headers["location"];
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,2 +1,3 @@
|
||||||
export * from "./gofile";
|
export * from "./gofile";
|
||||||
export * from "./qiwi";
|
export * from "./qiwi";
|
||||||
|
export * from "./datanodes";
|
||||||
|
|
|
@ -215,19 +215,15 @@ export class HydraApi {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async revalidateAccessTokenIfExpired() {
|
public static async refreshToken() {
|
||||||
const now = new Date();
|
const { accessToken, expiresIn } = await this.instance
|
||||||
|
.post<{ accessToken: string; expiresIn: number }>(`/auth/refresh`, {
|
||||||
if (this.userAuth.expirationTimestamp < now.getTime()) {
|
|
||||||
try {
|
|
||||||
const response = await this.instance.post(`/auth/refresh`, {
|
|
||||||
refreshToken: this.userAuth.refreshToken,
|
refreshToken: this.userAuth.refreshToken,
|
||||||
});
|
})
|
||||||
|
.then((response) => response.data);
|
||||||
const { accessToken, expiresIn } = response.data;
|
|
||||||
|
|
||||||
const tokenExpirationTimestamp =
|
const tokenExpirationTimestamp =
|
||||||
now.getTime() +
|
Date.now() +
|
||||||
this.secondsToMilliseconds(expiresIn) -
|
this.secondsToMilliseconds(expiresIn) -
|
||||||
this.EXPIRATION_OFFSET_IN_MS;
|
this.EXPIRATION_OFFSET_IN_MS;
|
||||||
|
|
||||||
|
@ -247,6 +243,14 @@ export class HydraApi {
|
||||||
},
|
},
|
||||||
["id"]
|
["id"]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return { accessToken, expiresIn };
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async revalidateAccessTokenIfExpired() {
|
||||||
|
if (this.userAuth.expirationTimestamp < Date.now()) {
|
||||||
|
try {
|
||||||
|
await this.refreshToken();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.handleUnauthorizedError(err);
|
this.handleUnauthorizedError(err);
|
||||||
}
|
}
|
||||||
|
@ -261,7 +265,7 @@ export class HydraApi {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static handleUnauthorizedError = (err) => {
|
private static readonly handleUnauthorizedError = (err) => {
|
||||||
if (err instanceof AxiosError && err.response?.status === 401) {
|
if (err instanceof AxiosError && err.response?.status === 401) {
|
||||||
logger.error(
|
logger.error(
|
||||||
"401 - Current credentials:",
|
"401 - Current credentials:",
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {
|
||||||
shell,
|
shell,
|
||||||
} from "electron";
|
} from "electron";
|
||||||
import { is } from "@electron-toolkit/utils";
|
import { is } from "@electron-toolkit/utils";
|
||||||
import i18next, { t } from "i18next";
|
import { t } from "i18next";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import icon from "@resources/icon.png?asset";
|
import icon from "@resources/icon.png?asset";
|
||||||
import trayIcon from "@resources/tray-icon.png?asset";
|
import trayIcon from "@resources/tray-icon.png?asset";
|
||||||
|
@ -17,6 +17,7 @@ import { gameRepository, userPreferencesRepository } from "@main/repository";
|
||||||
import { IsNull, Not } from "typeorm";
|
import { IsNull, Not } from "typeorm";
|
||||||
import { HydraApi } from "./hydra-api";
|
import { HydraApi } from "./hydra-api";
|
||||||
import UserAgent from "user-agents";
|
import UserAgent from "user-agents";
|
||||||
|
import { AuthPage } from "@shared";
|
||||||
|
|
||||||
export class WindowManager {
|
export class WindowManager {
|
||||||
public static mainWindow: Electron.BrowserWindow | null = null;
|
public static mainWindow: Electron.BrowserWindow | null = null;
|
||||||
|
@ -142,7 +143,7 @@ export class WindowManager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static openAuthWindow() {
|
public static openAuthWindow(page: AuthPage, searchParams: URLSearchParams) {
|
||||||
if (this.mainWindow) {
|
if (this.mainWindow) {
|
||||||
const authWindow = new BrowserWindow({
|
const authWindow = new BrowserWindow({
|
||||||
width: 600,
|
width: 600,
|
||||||
|
@ -164,12 +165,8 @@ export class WindowManager {
|
||||||
|
|
||||||
if (!app.isPackaged) authWindow.webContents.openDevTools();
|
if (!app.isPackaged) authWindow.webContents.openDevTools();
|
||||||
|
|
||||||
const searchParams = new URLSearchParams({
|
|
||||||
lng: i18next.language,
|
|
||||||
});
|
|
||||||
|
|
||||||
authWindow.loadURL(
|
authWindow.loadURL(
|
||||||
`${import.meta.env.MAIN_VITE_AUTH_URL}/?${searchParams.toString()}`
|
`${import.meta.env.MAIN_VITE_AUTH_URL}${page}?${searchParams.toString()}`
|
||||||
);
|
);
|
||||||
|
|
||||||
authWindow.once("ready-to-show", () => {
|
authWindow.once("ready-to-show", () => {
|
||||||
|
@ -181,6 +178,13 @@ export class WindowManager {
|
||||||
authWindow.close();
|
authWindow.close();
|
||||||
|
|
||||||
HydraApi.handleExternalAuth(url);
|
HydraApi.handleExternalAuth(url);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (url.startsWith("hydralauncher://update-account")) {
|
||||||
|
authWindow.close();
|
||||||
|
|
||||||
|
WindowManager.mainWindow?.webContents.send("on-account-updated");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ import type {
|
||||||
SeedingStatus,
|
SeedingStatus,
|
||||||
GameAchievement,
|
GameAchievement,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
import type { CatalogueCategory } from "@shared";
|
import type { AuthPage, CatalogueCategory } from "@shared";
|
||||||
import type { AxiosProgressEvent } from "axios";
|
import type { AxiosProgressEvent } from "axios";
|
||||||
|
|
||||||
contextBridge.exposeInMainWorld("electron", {
|
contextBridge.exposeInMainWorld("electron", {
|
||||||
|
@ -291,13 +291,19 @@ contextBridge.exposeInMainWorld("electron", {
|
||||||
|
|
||||||
/* Auth */
|
/* Auth */
|
||||||
signOut: () => ipcRenderer.invoke("signOut"),
|
signOut: () => ipcRenderer.invoke("signOut"),
|
||||||
openAuthWindow: () => ipcRenderer.invoke("openAuthWindow"),
|
openAuthWindow: (page: AuthPage) =>
|
||||||
|
ipcRenderer.invoke("openAuthWindow", page),
|
||||||
getSessionHash: () => ipcRenderer.invoke("getSessionHash"),
|
getSessionHash: () => ipcRenderer.invoke("getSessionHash"),
|
||||||
onSignIn: (cb: () => void) => {
|
onSignIn: (cb: () => void) => {
|
||||||
const listener = (_event: Electron.IpcRendererEvent) => cb();
|
const listener = (_event: Electron.IpcRendererEvent) => cb();
|
||||||
ipcRenderer.on("on-signin", listener);
|
ipcRenderer.on("on-signin", listener);
|
||||||
return () => ipcRenderer.removeListener("on-signin", listener);
|
return () => ipcRenderer.removeListener("on-signin", listener);
|
||||||
},
|
},
|
||||||
|
onAccountUpdated: (cb: () => void) => {
|
||||||
|
const listener = (_event: Electron.IpcRendererEvent) => cb();
|
||||||
|
ipcRenderer.on("on-account-updated", listener);
|
||||||
|
return () => ipcRenderer.removeListener("on-account-updated", listener);
|
||||||
|
},
|
||||||
onSignOut: (cb: () => void) => {
|
onSignOut: (cb: () => void) => {
|
||||||
const listener = (_event: Electron.IpcRendererEvent) => cb();
|
const listener = (_event: Electron.IpcRendererEvent) => cb();
|
||||||
ipcRenderer.on("on-signout", listener);
|
ipcRenderer.on("on-signout", listener);
|
||||||
|
|
|
@ -7,6 +7,7 @@ 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";
|
||||||
import { Avatar } from "../avatar/avatar";
|
import { Avatar } from "../avatar/avatar";
|
||||||
|
import { AuthPage } from "@shared";
|
||||||
|
|
||||||
const LONG_POLLING_INTERVAL = 120_000;
|
const LONG_POLLING_INTERVAL = 120_000;
|
||||||
|
|
||||||
|
@ -26,11 +27,11 @@ export function SidebarProfile() {
|
||||||
|
|
||||||
const handleProfileClick = () => {
|
const handleProfileClick = () => {
|
||||||
if (userDetails === null) {
|
if (userDetails === null) {
|
||||||
window.electron.openAuthWindow();
|
window.electron.openAuthWindow(AuthPage.SignIn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
navigate(`/profile/${userDetails!.id}`);
|
navigate(`/profile/${userDetails.id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -8,6 +8,7 @@ export const DOWNLOADER_NAME = {
|
||||||
[Downloader.Gofile]: "Gofile",
|
[Downloader.Gofile]: "Gofile",
|
||||||
[Downloader.PixelDrain]: "PixelDrain",
|
[Downloader.PixelDrain]: "PixelDrain",
|
||||||
[Downloader.Qiwi]: "Qiwi",
|
[Downloader.Qiwi]: "Qiwi",
|
||||||
|
[Downloader.Datanodes]: "Datanodes",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120;
|
export const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120;
|
||||||
|
|
5
src/renderer/src/declaration.d.ts
vendored
5
src/renderer/src/declaration.d.ts
vendored
|
@ -1,4 +1,4 @@
|
||||||
import type { CatalogueCategory } from "@shared";
|
import type { AuthPage, CatalogueCategory } from "@shared";
|
||||||
import type {
|
import type {
|
||||||
AppUpdaterEvent,
|
AppUpdaterEvent,
|
||||||
Game,
|
Game,
|
||||||
|
@ -208,9 +208,10 @@ declare global {
|
||||||
|
|
||||||
/* Auth */
|
/* Auth */
|
||||||
signOut: () => Promise<void>;
|
signOut: () => Promise<void>;
|
||||||
openAuthWindow: () => Promise<void>;
|
openAuthWindow: (page: AuthPage) => Promise<void>;
|
||||||
getSessionHash: () => Promise<string | null>;
|
getSessionHash: () => Promise<string | null>;
|
||||||
onSignIn: (cb: () => void) => () => Electron.IpcRenderer;
|
onSignIn: (cb: () => void) => () => Electron.IpcRenderer;
|
||||||
|
onAccountUpdated: (cb: () => void) => () => Electron.IpcRenderer;
|
||||||
onSignOut: (cb: () => void) => () => Electron.IpcRenderer;
|
onSignOut: (cb: () => void) => () => Electron.IpcRenderer;
|
||||||
|
|
||||||
/* User */
|
/* User */
|
||||||
|
|
|
@ -10,7 +10,7 @@ import { Sidebar } from "./sidebar/sidebar";
|
||||||
import * as styles from "./game-details.css";
|
import * as styles from "./game-details.css";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { cloudSyncContext, gameDetailsContext } from "@renderer/context";
|
import { cloudSyncContext, gameDetailsContext } from "@renderer/context";
|
||||||
import { steamUrlBuilder } from "@shared";
|
import { AuthPage, steamUrlBuilder } from "@shared";
|
||||||
|
|
||||||
import cloudIconAnimated from "@renderer/assets/icons/cloud-animated.gif";
|
import cloudIconAnimated from "@renderer/assets/icons/cloud-animated.gif";
|
||||||
import { useUserDetails } from "@renderer/hooks";
|
import { useUserDetails } from "@renderer/hooks";
|
||||||
|
@ -69,7 +69,7 @@ export function GameDetailsContent() {
|
||||||
});
|
});
|
||||||
|
|
||||||
const backgroundColor = output
|
const backgroundColor = output
|
||||||
? (new Color(output).darken(0.7).toString() as string)
|
? new Color(output).darken(0.7).toString()
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
setGameColor(backgroundColor);
|
setGameColor(backgroundColor);
|
||||||
|
@ -101,7 +101,7 @@ export function GameDetailsContent() {
|
||||||
|
|
||||||
const handleCloudSaveButtonClick = () => {
|
const handleCloudSaveButtonClick = () => {
|
||||||
if (!userDetails) {
|
if (!userDetails) {
|
||||||
window.electron.openAuthWindow();
|
window.electron.openAuthWindow(AuthPage.SignIn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { ChevronDownIcon } from "@primer/octicons-react";
|
import { ChevronDownIcon } from "@primer/octicons-react";
|
||||||
import { useRef, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import * as styles from "./sidebar-section.css";
|
import * as styles from "./sidebar-section.css";
|
||||||
|
|
||||||
|
@ -11,6 +11,15 @@ export interface SidebarSectionProps {
|
||||||
export function SidebarSection({ title, children }: SidebarSectionProps) {
|
export function SidebarSection({ title, children }: SidebarSectionProps) {
|
||||||
const content = useRef<HTMLDivElement>(null);
|
const content = useRef<HTMLDivElement>(null);
|
||||||
const [isOpen, setIsOpen] = useState(true);
|
const [isOpen, setIsOpen] = useState(true);
|
||||||
|
const [height, setHeight] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (content.current && content.current.scrollHeight !== height) {
|
||||||
|
setHeight(isOpen ? content.current.scrollHeight : 0);
|
||||||
|
} else if (!isOpen) {
|
||||||
|
setHeight(0);
|
||||||
|
}
|
||||||
|
}, [isOpen, children, height]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -26,7 +35,7 @@ export function SidebarSection({ title, children }: SidebarSectionProps) {
|
||||||
<div
|
<div
|
||||||
ref={content}
|
ref={content}
|
||||||
style={{
|
style={{
|
||||||
maxHeight: isOpen ? `${content.current?.scrollHeight}px` : "0",
|
maxHeight: `${height}px`,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
transition: "max-height 0.4s cubic-bezier(0, 1, 0, 1)",
|
transition: "max-height 0.4s cubic-bezier(0, 1, 0, 1)",
|
||||||
position: "relative",
|
position: "relative",
|
||||||
|
|
|
@ -225,6 +225,24 @@ export const link = style({
|
||||||
color: vars.color.body,
|
color: vars.color.body,
|
||||||
":hover": {
|
":hover": {
|
||||||
textDecoration: "underline",
|
textDecoration: "underline",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const gridSorting = style({
|
||||||
|
display: "flex", // Usa flexbox para organizar o layout
|
||||||
|
justifyContent: "space-between", // Espaça o label e os botões
|
||||||
|
alignItems: "center", // Centraliza verticalmente
|
||||||
|
marginBottom: `${SPACING_UNIT * 2}px`,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const sortOption = style({
|
||||||
|
transition: "all ease 0.2s",
|
||||||
|
display: "inline-flex", // Altera para inline-flex para alinhar itens no botão
|
||||||
|
alignItems: "center", // Centraliza o ícone e o texto verticalmente
|
||||||
|
gap: "10px", // Define o espaçamento entre o ícone e o texto
|
||||||
|
color: vars.color.body,
|
||||||
|
":hover": {
|
||||||
|
color: "white",
|
||||||
cursor: "pointer",
|
cursor: "pointer",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -236,3 +254,25 @@ export const gameCardStats = style({
|
||||||
flexShrink: "0",
|
flexShrink: "0",
|
||||||
flexGrow: "0",
|
flexGrow: "0",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const selectedSortOption = style({
|
||||||
|
transition: "all ease 0.2s",
|
||||||
|
display: "inline-flex", // Altera para inline-flex para alinhar itens no botão
|
||||||
|
alignItems: "center", // Centraliza o ícone e o texto verticalmente
|
||||||
|
color: "white",
|
||||||
|
gap: "10px", // Define o espaçamento entre o ícone e o texto
|
||||||
|
":hover": {
|
||||||
|
cursor: "pointer",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const sortOptionsWrapper = style({
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
gap: "5px",
|
||||||
|
});
|
||||||
|
|
||||||
|
export const sortDivider = style({
|
||||||
|
border: `0.5px solid ${vars.color.body}`,
|
||||||
|
margin: "0px 5px",
|
||||||
|
});
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
import { userProfileContext } from "@renderer/context";
|
import { userProfileContext } from "@renderer/context";
|
||||||
import { useContext, useEffect, useMemo, useRef, useState } from "react";
|
import { useContext, useEffect, useMemo, useState, useRef } from "react";
|
||||||
import { ProfileHero } from "../profile-hero/profile-hero";
|
import { ProfileHero } from "../profile-hero/profile-hero";
|
||||||
import { useAppDispatch, useFormat } from "@renderer/hooks";
|
import { useAppDispatch, useFormat } from "@renderer/hooks";
|
||||||
import { setHeaderTitle } from "@renderer/features";
|
import { setHeaderTitle } from "@renderer/features";
|
||||||
import { SPACING_UNIT } from "@renderer/theme.css";
|
import { SPACING_UNIT } from "@renderer/theme.css";
|
||||||
import * as styles from "./profile-content.css";
|
import * as styles from "./profile-content.css";
|
||||||
import { TelescopeIcon } from "@primer/octicons-react";
|
import {
|
||||||
|
ClockIcon,
|
||||||
|
TelescopeIcon,
|
||||||
|
TrophyIcon,
|
||||||
|
HistoryIcon,
|
||||||
|
} from "@primer/octicons-react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { LockedProfile } from "./locked-profile";
|
import { LockedProfile } from "./locked-profile";
|
||||||
|
@ -14,6 +19,7 @@ import { FriendsBox } from "./friends-box";
|
||||||
import { RecentGamesBox } from "./recent-games-box";
|
import { RecentGamesBox } from "./recent-games-box";
|
||||||
import { UserStatsBox } from "./user-stats-box";
|
import { UserStatsBox } from "./user-stats-box";
|
||||||
import { UserLibraryGameCard } from "./user-library-game-card";
|
import { UserLibraryGameCard } from "./user-library-game-card";
|
||||||
|
import { sortBy } from "lodash-es";
|
||||||
|
|
||||||
const GAME_STATS_ANIMATION_DURATION_IN_MS = 3500;
|
const GAME_STATS_ANIMATION_DURATION_IN_MS = 3500;
|
||||||
|
|
||||||
|
@ -27,6 +33,8 @@ export function ProfileContent() {
|
||||||
|
|
||||||
const { t } = useTranslation("user_profile");
|
const { t } = useTranslation("user_profile");
|
||||||
|
|
||||||
|
const [sortOption, setSortOption] = useState("lastPlayed"); // Estado para o critério de ordenação
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch(setHeaderTitle(""));
|
dispatch(setHeaderTitle(""));
|
||||||
|
|
||||||
|
@ -72,6 +80,25 @@ export function ProfileContent() {
|
||||||
return userProfile?.relation?.status === "ACCEPTED";
|
return userProfile?.relation?.status === "ACCEPTED";
|
||||||
}, [userProfile]);
|
}, [userProfile]);
|
||||||
|
|
||||||
|
const sortGames = (games) => {
|
||||||
|
if (sortOption === "playtime") {
|
||||||
|
return sortBy(games, (game) => -game.playTimeInSeconds);
|
||||||
|
} else if (sortOption === "achievements") {
|
||||||
|
return sortBy(games, (game) => {
|
||||||
|
return game.achievementCount > 0
|
||||||
|
? -(game.unlockedAchievementCount / game.achievementCount)
|
||||||
|
: 0;
|
||||||
|
});
|
||||||
|
} else if (sortOption === "lastPlayed") {
|
||||||
|
return sortBy(games, (game) => {
|
||||||
|
return game.lastTimePlayed
|
||||||
|
? -new Date(game.lastTimePlayed).getTime()
|
||||||
|
: 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return games;
|
||||||
|
};
|
||||||
|
|
||||||
const content = useMemo(() => {
|
const content = useMemo(() => {
|
||||||
if (!userProfile) return null;
|
if (!userProfile) return null;
|
||||||
|
|
||||||
|
@ -87,6 +114,8 @@ export function ProfileContent() {
|
||||||
|
|
||||||
const shouldShowRightContent = hasGames || userProfile.friends.length > 0;
|
const shouldShowRightContent = hasGames || userProfile.friends.length > 0;
|
||||||
|
|
||||||
|
const sortedGames = sortGames(userProfile.libraryGames || []); // Ordena os jogos conforme o critério
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
style={{
|
style={{
|
||||||
|
@ -116,8 +145,51 @@ export function ProfileContent() {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.gridSorting}>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="sort-options">Ordenar por: </label>
|
||||||
|
</div>
|
||||||
|
<div className={styles.sortOptionsWrapper}>
|
||||||
|
<button
|
||||||
|
className={`${sortOption === "lastPlayed" ? styles.selectedSortOption : styles.sortOption}`}
|
||||||
|
onClick={() => setSortOption("lastPlayed")}
|
||||||
|
onKeyDown={(e) =>
|
||||||
|
e.key === "Enter" && setSortOption("lastPlayed")
|
||||||
|
} // Add keyboard support
|
||||||
|
tabIndex={0} // Optional if you keep using <span>
|
||||||
|
>
|
||||||
|
<HistoryIcon size={14} />
|
||||||
|
Jogados recentemente
|
||||||
|
</button>
|
||||||
|
<div className={styles.sortDivider} />
|
||||||
|
<button
|
||||||
|
className={`${sortOption === "playtime" ? styles.selectedSortOption : styles.sortOption}`}
|
||||||
|
onClick={() => setSortOption("playtime")}
|
||||||
|
onKeyDown={(e) =>
|
||||||
|
e.key === "Enter" && setSortOption("playtime")
|
||||||
|
} // Add keyboard support
|
||||||
|
tabIndex={0} // Optional if you keep using <span>
|
||||||
|
>
|
||||||
|
<ClockIcon size={14} />
|
||||||
|
Tempo jogado
|
||||||
|
</button>
|
||||||
|
<div className={styles.sortDivider} />
|
||||||
|
<button
|
||||||
|
className={`${sortOption === "achievements" ? styles.selectedSortOption : styles.sortOption}`}
|
||||||
|
onClick={() => setSortOption("achievements")}
|
||||||
|
onKeyDown={(e) =>
|
||||||
|
e.key === "Enter" && setSortOption("achievements")
|
||||||
|
} // Add keyboard support
|
||||||
|
tabIndex={0} // Optional if you keep using <span>
|
||||||
|
>
|
||||||
|
<TrophyIcon size={14} />
|
||||||
|
Conquistas obtidas
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ul className={styles.gamesGrid}>
|
<ul className={styles.gamesGrid}>
|
||||||
{userProfile?.libraryGames?.map((game) => (
|
{sortedGames.map((game) => (
|
||||||
<UserLibraryGameCard
|
<UserLibraryGameCard
|
||||||
game={game}
|
game={game}
|
||||||
key={game.objectId}
|
key={game.objectId}
|
||||||
|
@ -150,6 +222,7 @@ export function ProfileContent() {
|
||||||
t,
|
t,
|
||||||
navigate,
|
navigate,
|
||||||
statsIndex,
|
statsIndex,
|
||||||
|
sortOption,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -5,14 +5,7 @@ import { SPACING_UNIT, vars } from "../../theme.css";
|
||||||
export const form = style({
|
export const form = style({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
gap: `${SPACING_UNIT}px`,
|
gap: `${SPACING_UNIT * 3}px`,
|
||||||
});
|
|
||||||
|
|
||||||
export const blockedUserAvatar = style({
|
|
||||||
width: "32px",
|
|
||||||
height: "32px",
|
|
||||||
borderRadius: "4px",
|
|
||||||
filter: "grayscale(100%)",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export const blockedUser = style({
|
export const blockedUser = style({
|
||||||
|
@ -43,5 +36,4 @@ export const blockedUsersList = style({
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
alignItems: "flex-start",
|
alignItems: "flex-start",
|
||||||
gap: `${SPACING_UNIT}px`,
|
gap: `${SPACING_UNIT}px`,
|
||||||
marginTop: `${SPACING_UNIT}px`,
|
|
||||||
});
|
});
|
291
src/renderer/src/pages/settings/settings-account.tsx
Normal file
291
src/renderer/src/pages/settings/settings-account.tsx
Normal file
|
@ -0,0 +1,291 @@
|
||||||
|
import { Avatar, Button, SelectField } from "@renderer/components";
|
||||||
|
import { SPACING_UNIT } from "@renderer/theme.css";
|
||||||
|
import { Controller, useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import * as styles from "./settings-account.css";
|
||||||
|
import { useDate, useToast, useUserDetails } from "@renderer/hooks";
|
||||||
|
import { useCallback, useContext, useEffect, useState } from "react";
|
||||||
|
import {
|
||||||
|
CloudIcon,
|
||||||
|
KeyIcon,
|
||||||
|
MailIcon,
|
||||||
|
XCircleFillIcon,
|
||||||
|
} from "@primer/octicons-react";
|
||||||
|
import { settingsContext } from "@renderer/context";
|
||||||
|
import { AuthPage } from "@shared";
|
||||||
|
|
||||||
|
interface FormValues {
|
||||||
|
profileVisibility: "PUBLIC" | "FRIENDS" | "PRIVATE";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SettingsAccount() {
|
||||||
|
const { t } = useTranslation("settings");
|
||||||
|
|
||||||
|
const [isUnblocking, setIsUnblocking] = useState(false);
|
||||||
|
|
||||||
|
const { showSuccessToast } = useToast();
|
||||||
|
|
||||||
|
const { blockedUsers, fetchBlockedUsers } = useContext(settingsContext);
|
||||||
|
|
||||||
|
const { formatDate } = useDate();
|
||||||
|
|
||||||
|
const {
|
||||||
|
control,
|
||||||
|
formState: { isSubmitting },
|
||||||
|
setValue,
|
||||||
|
handleSubmit,
|
||||||
|
} = useForm<FormValues>();
|
||||||
|
|
||||||
|
const {
|
||||||
|
userDetails,
|
||||||
|
hasActiveSubscription,
|
||||||
|
patchUser,
|
||||||
|
fetchUserDetails,
|
||||||
|
updateUserDetails,
|
||||||
|
unblockUser,
|
||||||
|
} = useUserDetails();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (userDetails?.profileVisibility) {
|
||||||
|
setValue("profileVisibility", userDetails.profileVisibility);
|
||||||
|
}
|
||||||
|
}, [userDetails, setValue]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const unsubscribe = window.electron.onAccountUpdated(() => {
|
||||||
|
fetchUserDetails().then((response) => {
|
||||||
|
if (response) {
|
||||||
|
updateUserDetails(response);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
showSuccessToast(t("account_data_updated_successfully"));
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
unsubscribe();
|
||||||
|
};
|
||||||
|
}, [fetchUserDetails, updateUserDetails]);
|
||||||
|
|
||||||
|
const visibilityOptions = [
|
||||||
|
{ value: "PUBLIC", label: t("public") },
|
||||||
|
{ value: "FRIENDS", label: t("friends_only") },
|
||||||
|
{ value: "PRIVATE", label: t("private") },
|
||||||
|
];
|
||||||
|
|
||||||
|
const onSubmit = async (values: FormValues) => {
|
||||||
|
await patchUser(values);
|
||||||
|
showSuccessToast(t("changes_saved"));
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUnblockClick = useCallback(
|
||||||
|
(id: string) => {
|
||||||
|
setIsUnblocking(true);
|
||||||
|
|
||||||
|
unblockUser(id)
|
||||||
|
.then(() => {
|
||||||
|
fetchBlockedUsers();
|
||||||
|
showSuccessToast(t("user_unblocked"));
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setIsUnblocking(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[unblockUser, fetchBlockedUsers, t, showSuccessToast]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getHydraCloudSectionContent = () => {
|
||||||
|
const hasSubscribedBefore = Boolean(userDetails?.subscription?.expiresAt);
|
||||||
|
const isRenewalActive = userDetails?.subscription?.status === "active";
|
||||||
|
|
||||||
|
if (!hasSubscribedBefore) {
|
||||||
|
return {
|
||||||
|
description: <small>{t("no_subscription")}</small>,
|
||||||
|
callToAction: t("become_subscriber"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasActiveSubscription) {
|
||||||
|
return {
|
||||||
|
description: isRenewalActive ? (
|
||||||
|
<>
|
||||||
|
<small>
|
||||||
|
{t("subscription_renews_on", {
|
||||||
|
date: formatDate(userDetails.subscription!.expiresAt!),
|
||||||
|
})}
|
||||||
|
</small>
|
||||||
|
<small>{t("bill_sent_until")}</small>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<small>{t("subscription_renew_cancelled")}</small>
|
||||||
|
<small>
|
||||||
|
{t("subscription_active_until", {
|
||||||
|
date: formatDate(userDetails!.subscription!.expiresAt!),
|
||||||
|
})}
|
||||||
|
</small>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
callToAction: t("manage_subscription"),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
description: (
|
||||||
|
<small>
|
||||||
|
{t("subscription_expired_at", {
|
||||||
|
date: formatDate(userDetails!.subscription!.expiresAt!),
|
||||||
|
})}
|
||||||
|
</small>
|
||||||
|
),
|
||||||
|
callToAction: t("renew_subscription"),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!userDetails) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form className={styles.form} onSubmit={handleSubmit(onSubmit)}>
|
||||||
|
<Controller
|
||||||
|
control={control}
|
||||||
|
name="profileVisibility"
|
||||||
|
render={({ field }) => {
|
||||||
|
const handleChange = (
|
||||||
|
event: React.ChangeEvent<HTMLSelectElement>
|
||||||
|
) => {
|
||||||
|
field.onChange(event);
|
||||||
|
handleSubmit(onSubmit)();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<SelectField
|
||||||
|
label={t("profile_visibility")}
|
||||||
|
value={field.value}
|
||||||
|
onChange={handleChange}
|
||||||
|
options={visibilityOptions.map((visiblity) => ({
|
||||||
|
key: visiblity.value,
|
||||||
|
value: visiblity.value,
|
||||||
|
label: visiblity.label,
|
||||||
|
}))}
|
||||||
|
disabled={isSubmitting}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<small>{t("profile_visibility_description")}</small>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h4>{t("current_email")}</h4>
|
||||||
|
<p>{userDetails?.email ?? t("no_email_account")}</p>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "start",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: `${SPACING_UNIT}px`,
|
||||||
|
marginTop: `${SPACING_UNIT * 2}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
theme="outline"
|
||||||
|
onClick={() => window.electron.openAuthWindow(AuthPage.UpdateEmail)}
|
||||||
|
>
|
||||||
|
<MailIcon />
|
||||||
|
{t("update_email")}
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
theme="outline"
|
||||||
|
onClick={() =>
|
||||||
|
window.electron.openAuthWindow(AuthPage.UpdatePassword)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<KeyIcon />
|
||||||
|
{t("update_password")}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: `${SPACING_UNIT * 2}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3>Hydra Cloud</h3>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: `${SPACING_UNIT}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{getHydraCloudSectionContent().description}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
style={{
|
||||||
|
placeSelf: "flex-start",
|
||||||
|
}}
|
||||||
|
theme="outline"
|
||||||
|
onClick={() => window.electron.openCheckout()}
|
||||||
|
>
|
||||||
|
<CloudIcon />
|
||||||
|
{getHydraCloudSectionContent().callToAction}
|
||||||
|
</Button>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: `${SPACING_UNIT * 2}px`,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<h3>{t("blocked_users")}</h3>
|
||||||
|
|
||||||
|
{blockedUsers.length > 0 ? (
|
||||||
|
<ul className={styles.blockedUsersList}>
|
||||||
|
{blockedUsers.map((user) => {
|
||||||
|
return (
|
||||||
|
<li key={user.id} className={styles.blockedUser}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
gap: `${SPACING_UNIT}px`,
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
style={{ filter: "grayscale(100%)" }}
|
||||||
|
size={32}
|
||||||
|
src={user.profileImageUrl}
|
||||||
|
alt={user.displayName}
|
||||||
|
/>
|
||||||
|
<span>{user.displayName}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={styles.unblockButton}
|
||||||
|
onClick={() => handleUnblockClick(user.id)}
|
||||||
|
disabled={isUnblocking}
|
||||||
|
>
|
||||||
|
<XCircleFillIcon />
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
) : (
|
||||||
|
<small>{t("no_users_blocked")}</small>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,139 +0,0 @@
|
||||||
import { SelectField } from "@renderer/components";
|
|
||||||
import { SPACING_UNIT } from "@renderer/theme.css";
|
|
||||||
import { Controller, useForm } from "react-hook-form";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import * as styles from "./settings-privacy.css";
|
|
||||||
import { useToast, useUserDetails } from "@renderer/hooks";
|
|
||||||
import { useCallback, useContext, useEffect, useState } from "react";
|
|
||||||
import { XCircleFillIcon } from "@primer/octicons-react";
|
|
||||||
import { settingsContext } from "@renderer/context";
|
|
||||||
|
|
||||||
interface FormValues {
|
|
||||||
profileVisibility: "PUBLIC" | "FRIENDS" | "PRIVATE";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SettingsPrivacy() {
|
|
||||||
const { t } = useTranslation("settings");
|
|
||||||
|
|
||||||
const [isUnblocking, setIsUnblocking] = useState(false);
|
|
||||||
|
|
||||||
const { showSuccessToast } = useToast();
|
|
||||||
|
|
||||||
const { blockedUsers, fetchBlockedUsers } = useContext(settingsContext);
|
|
||||||
|
|
||||||
const {
|
|
||||||
control,
|
|
||||||
formState: { isSubmitting },
|
|
||||||
setValue,
|
|
||||||
handleSubmit,
|
|
||||||
} = useForm<FormValues>();
|
|
||||||
|
|
||||||
const { patchUser, userDetails } = useUserDetails();
|
|
||||||
|
|
||||||
const { unblockUser } = useUserDetails();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (userDetails?.profileVisibility) {
|
|
||||||
setValue("profileVisibility", userDetails.profileVisibility);
|
|
||||||
}
|
|
||||||
}, [userDetails, setValue]);
|
|
||||||
|
|
||||||
const visibilityOptions = [
|
|
||||||
{ value: "PUBLIC", label: t("public") },
|
|
||||||
{ value: "FRIENDS", label: t("friends_only") },
|
|
||||||
{ value: "PRIVATE", label: t("private") },
|
|
||||||
];
|
|
||||||
|
|
||||||
const onSubmit = async (values: FormValues) => {
|
|
||||||
await patchUser(values);
|
|
||||||
showSuccessToast(t("changes_saved"));
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleUnblockClick = useCallback(
|
|
||||||
(id: string) => {
|
|
||||||
setIsUnblocking(true);
|
|
||||||
|
|
||||||
unblockUser(id)
|
|
||||||
.then(() => {
|
|
||||||
fetchBlockedUsers();
|
|
||||||
showSuccessToast(t("user_unblocked"));
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsUnblocking(false);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[unblockUser, fetchBlockedUsers, t, showSuccessToast]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<form className={styles.form} onSubmit={handleSubmit(onSubmit)}>
|
|
||||||
<Controller
|
|
||||||
control={control}
|
|
||||||
name="profileVisibility"
|
|
||||||
render={({ field }) => {
|
|
||||||
const handleChange = (
|
|
||||||
event: React.ChangeEvent<HTMLSelectElement>
|
|
||||||
) => {
|
|
||||||
field.onChange(event);
|
|
||||||
handleSubmit(onSubmit)();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<SelectField
|
|
||||||
label={t("profile_visibility")}
|
|
||||||
value={field.value}
|
|
||||||
onChange={handleChange}
|
|
||||||
options={visibilityOptions.map((visiblity) => ({
|
|
||||||
key: visiblity.value,
|
|
||||||
value: visiblity.value,
|
|
||||||
label: visiblity.label,
|
|
||||||
}))}
|
|
||||||
disabled={isSubmitting}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<small>{t("profile_visibility_description")}</small>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<h3 style={{ marginTop: `${SPACING_UNIT * 2}px` }}>
|
|
||||||
{t("blocked_users")}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<ul className={styles.blockedUsersList}>
|
|
||||||
{blockedUsers.map((user) => {
|
|
||||||
return (
|
|
||||||
<li key={user.id} className={styles.blockedUser}>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
display: "flex",
|
|
||||||
gap: `${SPACING_UNIT}px`,
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src={user.profileImageUrl!}
|
|
||||||
alt={user.displayName}
|
|
||||||
className={styles.blockedUserAvatar}
|
|
||||||
/>
|
|
||||||
<span>{user.displayName}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={styles.unblockButton}
|
|
||||||
onClick={() => handleUnblockClick(user.id)}
|
|
||||||
disabled={isUnblocking}
|
|
||||||
>
|
|
||||||
<XCircleFillIcon />
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</ul>
|
|
||||||
</form>
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ import {
|
||||||
SettingsContextConsumer,
|
SettingsContextConsumer,
|
||||||
SettingsContextProvider,
|
SettingsContextProvider,
|
||||||
} from "@renderer/context";
|
} from "@renderer/context";
|
||||||
import { SettingsPrivacy } from "./settings-privacy";
|
import { SettingsAccount } from "./settings-account";
|
||||||
import { useUserDetails } from "@renderer/hooks";
|
import { useUserDetails } from "@renderer/hooks";
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ export default function Settings() {
|
||||||
"Real-Debrid",
|
"Real-Debrid",
|
||||||
];
|
];
|
||||||
|
|
||||||
if (userDetails) return [...categories, t("privacy")];
|
if (userDetails) return [...categories, t("account")];
|
||||||
return categories;
|
return categories;
|
||||||
}, [userDetails, t]);
|
}, [userDetails, t]);
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ export default function Settings() {
|
||||||
return <SettingsRealDebrid />;
|
return <SettingsRealDebrid />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return <SettingsPrivacy />;
|
return <SettingsAccount />;
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -4,6 +4,7 @@ export enum Downloader {
|
||||||
Gofile,
|
Gofile,
|
||||||
PixelDrain,
|
PixelDrain,
|
||||||
Qiwi,
|
Qiwi,
|
||||||
|
Datanodes,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum DownloadSourceStatus {
|
export enum DownloadSourceStatus {
|
||||||
|
@ -41,3 +42,9 @@ export enum Cracker {
|
||||||
rle = "RLE",
|
rle = "RLE",
|
||||||
razor1911 = "RAZOR1911",
|
razor1911 = "RAZOR1911",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum AuthPage {
|
||||||
|
SignIn = "/",
|
||||||
|
UpdateEmail = "/update-email",
|
||||||
|
UpdatePassword = "/update-password",
|
||||||
|
}
|
||||||
|
|
|
@ -87,6 +87,7 @@ export const getDownloadersForUri = (uri: string) => {
|
||||||
|
|
||||||
if (uri.startsWith("https://pixeldrain.com")) return [Downloader.PixelDrain];
|
if (uri.startsWith("https://pixeldrain.com")) return [Downloader.PixelDrain];
|
||||||
if (uri.startsWith("https://qiwi.gg")) return [Downloader.Qiwi];
|
if (uri.startsWith("https://qiwi.gg")) return [Downloader.Qiwi];
|
||||||
|
if (uri.startsWith("https://datanodes.to")) return [Downloader.Datanodes];
|
||||||
|
|
||||||
if (realDebridHosts.some((host) => uri.startsWith(host)))
|
if (realDebridHosts.some((host) => uri.startsWith(host)))
|
||||||
return [Downloader.RealDebrid];
|
return [Downloader.RealDebrid];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue