From 82b1d710c2da14002e80f416a830b9e6b5f4954b Mon Sep 17 00:00:00 2001 From: Shisuys Date: Fri, 3 Jan 2025 16:58:37 -0300 Subject: [PATCH 01/36] Datanodes support --- .../services/download/download-manager.ts | 12 ++++- src/main/services/hosters/datanodes.ts | 51 +++++++++++++++++++ src/main/services/hosters/index.ts | 1 + src/renderer/src/constants.ts | 1 + src/shared/constants.ts | 1 + src/shared/index.ts | 1 + 6 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 src/main/services/hosters/datanodes.ts diff --git a/src/main/services/download/download-manager.ts b/src/main/services/download/download-manager.ts index 0d9f5cbb7..134a74e60 100644 --- a/src/main/services/download/download-manager.ts +++ b/src/main/services/download/download-manager.ts @@ -8,7 +8,7 @@ import { } from "@main/repository"; import { publishDownloadCompleteNotification } from "../notifications"; import type { DownloadProgress } from "@types"; -import { GofileApi, QiwiApi } from "../hosters"; +import { GofileApi, QiwiApi, DatanodesApi } from "../hosters"; import { PythonRPC } from "../python-rpc"; import { LibtorrentPayload, @@ -277,6 +277,16 @@ export class DownloadManager { 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: return { action: "start", diff --git a/src/main/services/hosters/datanodes.ts b/src/main/services/hosters/datanodes.ts new file mode 100644 index 000000000..28bd026e2 --- /dev/null +++ b/src/main/services/hosters/datanodes.ts @@ -0,0 +1,51 @@ +import axios, { AxiosResponse } from "axios"; + +export class DatanodesApi { + private static session = axios.create({}); + + public static async getDownloadUrl(downloadUrl: string): Promise { + const parsedUrl = new URL(downloadUrl); + const pathSegments = parsedUrl.pathname.split("/"); + + const fileCode = decodeURIComponent(pathSegments[1]); + const fileName = decodeURIComponent(pathSegments[pathSegments.length - 1]); + + const headers: Record = { + "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", + }; + + const payload = new URLSearchParams({ + op: "download2", + id: fileCode, + rand: "", + referer: "https://datanodes.to/download", + method_free: "Free Download >>", + method_premium: "", + adblock_detected: "", + }); + + const config = { + headers, + maxRedirects: 0, + validateStatus: (status: number) => status === 302 || status < 400, + }; + + const response: AxiosResponse = await DatanodesApi.session.post( + "https://datanodes.to/download", + payload, + config + ); + + if (response.status === 302) { + return response.headers["location"]; + } + + return ""; + } +} diff --git a/src/main/services/hosters/index.ts b/src/main/services/hosters/index.ts index 4c5b18035..8cff7bd25 100644 --- a/src/main/services/hosters/index.ts +++ b/src/main/services/hosters/index.ts @@ -1,2 +1,3 @@ export * from "./gofile"; export * from "./qiwi"; +export * from "./datanodes"; diff --git a/src/renderer/src/constants.ts b/src/renderer/src/constants.ts index 745418379..d0797caf2 100644 --- a/src/renderer/src/constants.ts +++ b/src/renderer/src/constants.ts @@ -8,6 +8,7 @@ export const DOWNLOADER_NAME = { [Downloader.Gofile]: "Gofile", [Downloader.PixelDrain]: "PixelDrain", [Downloader.Qiwi]: "Qiwi", + [Downloader.Datanodes]: "Datanodes", }; export const MAX_MINUTES_TO_SHOW_IN_PLAYTIME = 120; diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 2d313abb9..6b332d40a 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -4,6 +4,7 @@ export enum Downloader { Gofile, PixelDrain, Qiwi, + Datanodes, } export enum DownloadSourceStatus { diff --git a/src/shared/index.ts b/src/shared/index.ts index 858683919..7d612a170 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -87,6 +87,7 @@ export const getDownloadersForUri = (uri: string) => { if (uri.startsWith("https://pixeldrain.com")) return [Downloader.PixelDrain]; 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))) return [Downloader.RealDebrid]; From 87acdea5abb4a495d668417807e4b5d34ac05d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=F0=9D=97=A6=F0=9D=97=B5=F0=9D=97=9F=F0=9D=97=B2?= =?UTF-8?q?=F0=9D=97=BF=F0=9D=97=A3?= <75412448+mikropsoft@users.noreply.github.com> Date: Sun, 5 Jan 2025 21:08:52 +0300 Subject: [PATCH 02/36] Update TR Locales --- src/locales/tr/translation.json | 425 +++++++++++++++++++++++++++----- 1 file changed, 361 insertions(+), 64 deletions(-) diff --git a/src/locales/tr/translation.json b/src/locales/tr/translation.json index 6757fdf51..8d0e7811f 100644 --- a/src/locales/tr/translation.json +++ b/src/locales/tr/translation.json @@ -1,131 +1,428 @@ { "language_name": "Türkçe", + "app": { + "successfully_signed_in": "Başarıyla giriş yapıldı" + }, "home": { - "featured": "Öne çıkan", - "surprise_me": "Şaşırt beni", - "no_results": "Sonuç bulunamadı" + "featured": "Öne Çıkanlar", + "surprise_me": "Beni Şaşırt", + "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": { "catalogue": "Katalog", - "downloads": "İndirmeler", + "downloads": "İndirilenler", "settings": "Ayarlar", - "my_library": "Kütüphane", - "downloading_metadata": "{{title}} (Metadata indiriliyor…)", - "paused": "{{title}} (Duraklatıldı)", + "my_library": "Kütüphanem", + "downloading_metadata": "{{title}} (Meta verileri indiriliyor…)", + "paused": "{{title}} (Durduruldu)", "downloading": "{{title}} ({{percentage}} - İndiriliyor…)", "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": { - "search": "Ara", - "home": "Ana menü", + "search": "Oyunları ara", + "home": "Ana Sayfa", "catalogue": "Katalog", - "downloads": "İndirmeler", + "downloads": "İndirilenler", "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": { - "no_downloads_in_progress": "İndirilen bir şey yok", - "downloading_metadata": "{{title}} metadatası indiriliyor…", - "downloading": "{{title}} indiriliyor… ({{percentage}} tamamlandı) - Bitiş {{eta}} - {{speed}}" + "no_downloads_in_progress": "Devam eden indirme yok", + "downloading_metadata": "{{title}} meta verileri indiriliyor…", + "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": { - "next_page": "Sonraki sayfa", - "previous_page": "Önceki sayfa" + "search": "Filtrele…", + "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": { "open_download_options": "İndirme seçeneklerini aç", "download_options_zero": "İndirme seçeneği yok", "download_options_one": "{{count}} indirme seçeneği", "download_options_other": "{{count}} indirme seçeneği", - "updated_at": "{{updated_at}} güncellendi", - "install": "İndir", + "updated_at": "{{updated_at}} tarihinde güncellendi", + "install": "Yükle", "resume": "Devam et", - "pause": "Duraklat", + "pause": "Durdur", "cancel": "İptal et", - "remove": "Sil", - "space_left_on_disk": "Diskte {{space}} yer kaldı", - "eta": "Bitiş {{eta}}", - "downloading_metadata": "Metadata indiriliyor…", - "filter": "Repackleri filtrele", + "remove": "Kaldır", + "space_left_on_disk": "Diskte {{space}} boş alan kaldı", + "eta": "{{eta}} tahmini bitiş", + "calculating_eta": "Kalan süre hesaplanıyor…", + "downloading_metadata": "Meta veriler indiriliyor…", + "filter": "Paketleri filtrele", "requirements": "Sistem gereksinimleri", "minimum": "Minimum", "recommended": "Önerilen", - "release_date": "{{date}} tarihinde çıktı", - "publisher": "{{publisher}} tarihinde yayınlandı", - "hours": "saatler", - "minutes": "dakikalar", + "paused": "Durduruldu", + "release_date": "{{date}} tarihinde yayımlandı", + "publisher": "{{publisher}} tarafından yayımlandı", + "hours": "saat", + "minutes": "dakika", "amount_hours": "{{amount}} saat", "amount_minutes": "{{amount}} dakika", - "accuracy": "%{{accuracy}} doğruluk", + "accuracy": "{{accuracy}}% doğruluk", "add_to_library": "Kütüphaneye ekle", "remove_from_library": "Kütüphaneden kaldır", - "no_downloads": "İndirme yok", - "play_time": "{{amount}} oynandı", - "last_time_played": "Son oynanan {{period}}", - "not_played_yet": "Bu {{title}} hiç oynanmadı", - "next_suggestion": "Sıradaki öneri", + "no_downloads": "İndirilebilir içerik yok", + "play_time": "{{amount}} süre oynandı", + "last_time_played": "Son oynama {{period}} önce", + "not_played_yet": "{{title}} henüz oynanmadı", + "next_suggestion": "Sonraki öneri", "play": "Oyna", - "deleting": "Installer siliniyor…", + "deleting": "Yükleyici siliniyor…", "close": "Kapat", - "playing_now": "Şimdi oynanıyor", + "playing_now": "Şu anda oynanıyor", "change": "Değiştir", - "repacks_modal_description": "İndirmek istediğiiniz repacki seçin", - "select_folder_hint": "Varsayılan klasörü değiştirmek için ulaşmanız gereken ayar", - "download_now": "Şimdi" + "repacks_modal_description": "İndirmek istediğiniz paketi seçin", + "select_folder_hint": "Varsayılan klasörü değiştirmek için <0>Ayarlar bölümüne gidin", + "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": { - "title": "Hydra'yı aktif et", - "installation_id": "Kurulum ID'si:", - "enter_activation_code": "Aktifleştirme kodunuzu girin", - "message": "Bunu nerede soracağınızı bilmiyorsanız, buna sahip olmamanız gerekiyor.", - "activate": "Aktif et", + "title": "Hydra'yı Aktive Et", + "installation_id": "Kurulum Kimliği:", + "enter_activation_code": "Aktivasyon kodunuzu girin", + "message": "Bunu nereden soracağınızı bilmiyorsanız, bu sizin için olmamalı.", + "activate": "Aktive Et", "loading": "Yükleniyor…" }, "downloads": { - "resume": "Devam et", + "resume": "Devam Et", "pause": "Duraklat", - "eta": "Bitiş {{eta}}", + "eta": "Tamamlama {{eta}}", "paused": "Duraklatıldı", "verifying": "Doğrulanıyor…", "completed": "Tamamlandı", - "cancel": "İptal et", - "filter": "Yüklü oyunları filtrele", + "removed": "İndirilmedi", + "cancel": "İptal Et", + "filter": "İndirilen oyunları filtrele", "remove": "Kaldır", "downloading_metadata": "Metadata indiriliyor…", - "deleting": "Installer siliniyor…", - "delete": "Installer'ı sil", + "deleting": "Yükleyici siliniyor…", + "delete": "Yükleyiciyi kaldır", "delete_modal_title": "Emin misiniz?", - "delete_modal_description": "Bu bilgisayarınızdan tüm kurulum dosyalarını silecek", - "install": "Kur" + "delete_modal_description": "Bu işlem, tüm kurulum dosyalarını bilgisayarınızdan kaldıracaktır", + "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": { "downloads_path": "İndirme yolu", "change": "Güncelle", "notifications": "Bildirimler", - "enable_download_notifications": "Bir indirme bittiğinde", - "enable_repack_list_notifications": "Yeni bir repack eklendiğinde" + "enable_download_notifications": "Bir indirme tamamlandığında", + "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 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": { "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_count_one": "{{count}} yeni repack eklendi", - "repack_count_other": "{{count}} yeni repack eklendi" + "repack_count_one": "{{count}} 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": { - "open": "Hydra'yı aç", + "open": "Hydra'yı Aç", "quit": "Çık" }, "game_card": { - "no_downloads": "İndirme mevcut değil" + "no_downloads": "İndirilebilir içerik bulunmuyor" }, "binary_not_found_modal": { - "title": "Programlar yüklü değil", - "description": "Sisteminizde Wine veya Lutris çalıştırılabiliri bulunamadı", - "instructions": "Oyunları düzgün şekilde çalıştırmak için Linux distronuza bunlardan birini nasıl yükleyebileceğinize bakın" + "title": "Programlar Yüklü Değil", + "description": "Wine veya Lutris çalıştırılabilir dosyaları sisteminizde bulunamadı", + "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": { - "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 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" } } + + + + + From ab2d8c351bfd02c02371b6b0a08d65450cb68b37 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sun, 5 Jan 2025 15:37:25 -0300 Subject: [PATCH 03/36] Removing excessive new lines --- src/locales/tr/translation.json | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/locales/tr/translation.json b/src/locales/tr/translation.json index 8d0e7811f..6fa89c033 100644 --- a/src/locales/tr/translation.json +++ b/src/locales/tr/translation.json @@ -421,8 +421,3 @@ "learn_more": "Daha Fazla Bilgi Edinin" } } - - - - - From 2a31c32cdaa571d7429692cd077e58ff6238bef9 Mon Sep 17 00:00:00 2001 From: Shisuys Date: Sun, 5 Jan 2025 17:32:09 -0300 Subject: [PATCH 04/36] Update datanodes.ts --- src/main/services/hosters/datanodes.ts | 33 +++++++++++--------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/main/services/hosters/datanodes.ts b/src/main/services/hosters/datanodes.ts index 28bd026e2..d77e7d514 100644 --- a/src/main/services/hosters/datanodes.ts +++ b/src/main/services/hosters/datanodes.ts @@ -1,7 +1,7 @@ import axios, { AxiosResponse } from "axios"; export class DatanodesApi { - private static session = axios.create({}); + private static readonly session = axios.create({}); public static async getDownloadUrl(downloadUrl: string): Promise { const parsedUrl = new URL(downloadUrl); @@ -10,16 +10,6 @@ export class DatanodesApi { const fileCode = decodeURIComponent(pathSegments[1]); const fileName = decodeURIComponent(pathSegments[pathSegments.length - 1]); - const headers: Record = { - "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", - }; - const payload = new URLSearchParams({ op: "download2", id: fileCode, @@ -30,16 +20,21 @@ export class DatanodesApi { adblock_detected: "", }); - const config = { - headers, - maxRedirects: 0, - validateStatus: (status: number) => status === 302 || status < 400, - }; - - const response: AxiosResponse = await DatanodesApi.session.post( + const response: AxiosResponse = await this.session.post( "https://datanodes.to/download", payload, - config + { + 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) { From cac2a7a70e3266992069026e6d30ecee55d24950 Mon Sep 17 00:00:00 2001 From: hydrasources Date: Tue, 7 Jan 2025 18:53:34 +0300 Subject: [PATCH 05/36] Update translation.json --- src/locales/ru/translation.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 741f5e16f..c513afeec 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -167,6 +167,9 @@ "loading_save_preview": "Поиск сохранений…", "wine_prefix": "Префикс Wine", "wine_prefix_description": "Префикс Wine, используемый для запуска этой игры", + "launch_options": "Параметры запуска", + "launch_options_description": "Опытные пользователи могут внести изменения в параметры запуска", + "launch_options_placeholder": "Параметр не указан ", "no_download_option_info": "Информация недоступна", "backup_deletion_failed": "Не удалось удалить резервную копию", "max_number_of_artifacts_reached": "Достигнуто максимальное количество резервных копий для этой игры", @@ -175,7 +178,11 @@ "select_folder": "Выбрать папку", "backup_from": "Резервная копия от {{date}}", "custom_backup_location_set": "Установлено настраиваемое местоположение резервной копии", - "no_directory_selected": "Не выбран каталог" + "no_directory_selected": "Не выбран каталог", + "no_write_permission": "Невозможно загрузить в эту директорию. Нажмите здесь, чтобы узнать больше." + "reset_achievements_title": "Вы уверены?", + "reset_achievements_success": "Достижения успешно сброшены", + "reset_achievements_error": "Не удалось сбросить достижения". }, "activation": { "title": "Активировать Hydra", From 317434f663fb61114b419adc8a67a30c700374c9 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:02:20 -0300 Subject: [PATCH 06/36] fix: lint error --- src/locales/ru/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index c513afeec..3bb324400 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -182,7 +182,7 @@ "no_write_permission": "Невозможно загрузить в эту директорию. Нажмите здесь, чтобы узнать больше." "reset_achievements_title": "Вы уверены?", "reset_achievements_success": "Достижения успешно сброшены", - "reset_achievements_error": "Не удалось сбросить достижения". + "reset_achievements_error": "Не удалось сбросить достижения" }, "activation": { "title": "Активировать Hydra", From a0a3697516c521bb8fa81cc5ef85e18312a70ae2 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:08:13 -0300 Subject: [PATCH 07/36] fix: missing comma --- src/locales/ru/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 3bb324400..d02d7797b 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -179,7 +179,7 @@ "backup_from": "Резервная копия от {{date}}", "custom_backup_location_set": "Установлено настраиваемое местоположение резервной копии", "no_directory_selected": "Не выбран каталог", - "no_write_permission": "Невозможно загрузить в эту директорию. Нажмите здесь, чтобы узнать больше." + "no_write_permission": "Невозможно загрузить в эту директорию. Нажмите здесь, чтобы узнать больше.", "reset_achievements_title": "Вы уверены?", "reset_achievements_success": "Достижения успешно сброшены", "reset_achievements_error": "Не удалось сбросить достижения" From 21a88b889f1a87d766c909ed7ed56cfe574f23ef Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 7 Jan 2025 13:45:31 -0300 Subject: [PATCH 08/36] chore: run prettier --- src/locales/ru/translation.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index d02d7797b..92008a5e0 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -167,8 +167,8 @@ "loading_save_preview": "Поиск сохранений…", "wine_prefix": "Префикс Wine", "wine_prefix_description": "Префикс Wine, используемый для запуска этой игры", - "launch_options": "Параметры запуска", - "launch_options_description": "Опытные пользователи могут внести изменения в параметры запуска", + "launch_options": "Параметры запуска", + "launch_options_description": "Опытные пользователи могут внести изменения в параметры запуска", "launch_options_placeholder": "Параметр не указан ", "no_download_option_info": "Информация недоступна", "backup_deletion_failed": "Не удалось удалить резервную копию", From 8fbe23e61cfcba955d148fd0d9df810e6da0daf1 Mon Sep 17 00:00:00 2001 From: Kelvin Date: Thu, 9 Jan 2025 00:58:25 -0300 Subject: [PATCH 09/36] readme translation pt-BR improvement --- docs/README.pt-BR.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/README.pt-BR.md b/docs/README.pt-BR.md index f9ba9d66f..6e65d3fbc 100644 --- a/docs/README.pt-BR.md +++ b/docs/README.pt-BR.md @@ -125,6 +125,10 @@ cd hydra yarn ``` +### Install OpenSSL 1.1 + +[OpenSSL 1.1](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe) é exigido pelo libtorrent em ambientes Windows. + ### 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/). From 392279c4e1d47f190026b62b3075a46f26799e1e Mon Sep 17 00:00:00 2001 From: Kelvin <90298223+KelvinDiasMoreira@users.noreply.github.com> Date: Thu, 9 Jan 2025 11:58:08 -0300 Subject: [PATCH 10/36] fix translate error --- docs/README.pt-BR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.pt-BR.md b/docs/README.pt-BR.md index 6e65d3fbc..ca5933fbc 100644 --- a/docs/README.pt-BR.md +++ b/docs/README.pt-BR.md @@ -125,7 +125,7 @@ cd hydra yarn ``` -### Install OpenSSL 1.1 +### Instale OpenSSL 1.1 [OpenSSL 1.1](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe) é exigido pelo libtorrent em ambientes Windows. From 09c1170407670385dc847648e8c49ed4b25322bb Mon Sep 17 00:00:00 2001 From: Kelvin <90298223+KelvinDiasMoreira@users.noreply.github.com> Date: Fri, 10 Jan 2025 14:45:19 -0300 Subject: [PATCH 11/36] added anchor tag --- docs/README.pt-BR.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README.pt-BR.md b/docs/README.pt-BR.md index ca5933fbc..ca9428544 100644 --- a/docs/README.pt-BR.md +++ b/docs/README.pt-BR.md @@ -125,7 +125,7 @@ cd hydra yarn ``` -### Instale OpenSSL 1.1 +### Instale OpenSSL 1.1 [OpenSSL 1.1](https://slproweb.com/download/Win64OpenSSL-1_1_1w.exe) é exigido pelo libtorrent em ambientes Windows. From af4fcb8f064a5f8bb6db5180ebe23c506e80e87a Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:49:11 -0300 Subject: [PATCH 12/36] feat: manage account buttons --- src/locales/en/translation.json | 13 +- src/locales/pt-BR/translation.json | 13 +- src/main/events/index.ts | 1 + src/main/events/misc/open-manage-account.ts | 25 ++ src/main/services/hydra-api.ts | 14 +- src/preload/index.ts | 3 + src/renderer/src/declaration.d.ts | 2 + ...privacy.css.ts => settings-account.css.ts} | 0 .../src/pages/settings/settings-account.tsx | 217 ++++++++++++++++++ .../src/pages/settings/settings-privacy.tsx | 139 ----------- src/renderer/src/pages/settings/settings.tsx | 6 +- src/types/index.ts | 2 + 12 files changed, 286 insertions(+), 149 deletions(-) create mode 100644 src/main/events/misc/open-manage-account.ts rename src/renderer/src/pages/settings/{settings-privacy.css.ts => settings-account.css.ts} (100%) create mode 100644 src/renderer/src/pages/settings/settings-account.tsx delete mode 100644 src/renderer/src/pages/settings/settings-privacy.tsx diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 4e3dcb37c..e55954bff 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -280,7 +280,18 @@ "launch_minimized": "Launch Hydra minimized", "disable_nsfw_alert": "Disable NSFW alert", "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": "Hydra Cloud subscription", + "subscription_active_until": "Your Hydra Cloud is active until {{date}}", + "subscription_not_active": "You don't have an active Hydra Cloud subscription", + "manage_account": "Manage account", + "manage_subscription": "Manage subscription", + "update_email": "Update email", + "update_password": "Update password", + "current_email": "Current email:", + "no_associated_email": "You don't have an associated email yet" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 2a80084f3..24ab46538 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -268,7 +268,18 @@ "launch_minimized": "Iniciar o Hydra minimizado", "disable_nsfw_alert": "Desativar alerta de conteúdo inapropriado", "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": "Assinatura Hydra Cloud", + "subscription_active_until": "Seu Hydra Cloud ficará ativo até {{date}}", + "subscription_not_active": "Você não possui uma assinatura Hydra Cloud ativa", + "manage_account": "Gerenciar conta", + "manage_subscription": "Gerenciar assinatura", + "update_email": "Atualizar email", + "update_password": "Atualizar senha", + "current_email": "Email atual:", + "no_associated_email": "Você ainda não associou nenhum email a sua conta" }, "notifications": { "download_complete": "Download concluído", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 25882c3fc..f0f882cad 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -30,6 +30,7 @@ import "./library/remove-game-from-library"; import "./library/select-game-wine-prefix"; import "./library/reset-game-achievements"; import "./misc/open-checkout"; +import "./misc/open-manage-account"; import "./misc/open-external"; import "./misc/show-open-dialog"; import "./misc/get-features"; diff --git a/src/main/events/misc/open-manage-account.ts b/src/main/events/misc/open-manage-account.ts new file mode 100644 index 000000000..2946b04b3 --- /dev/null +++ b/src/main/events/misc/open-manage-account.ts @@ -0,0 +1,25 @@ +import { shell } from "electron"; +import { registerEvent } from "../register-event"; +import { HydraApi, logger } from "@main/services"; +import { ManageAccountPage } from "@types"; + +const openManageAccount = async ( + _event: Electron.IpcMainInvokeEvent, + page: ManageAccountPage +) => { + try { + const { accessToken } = await HydraApi.refreshToken(); + + const params = new URLSearchParams({ + token: accessToken, + }); + + shell.openExternal( + `${import.meta.env.MAIN_VITE_AUTH_URL}/${page}?${params.toString()}` + ); + } catch (err) { + logger.error("Failed to open manage account", err); + } +}; + +registerEvent("openManageAccount", openManageAccount); diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 63dd9b16f..4ab20e801 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -215,16 +215,20 @@ export class HydraApi { } } + public static async refreshToken() { + return this.instance + .post<{ accessToken: string; expiresIn: number }>(`/auth/refresh`, { + refreshToken: this.userAuth.refreshToken, + }) + .then((response) => response.data); + } + private static async revalidateAccessTokenIfExpired() { const now = new Date(); if (this.userAuth.expirationTimestamp < now.getTime()) { try { - const response = await this.instance.post(`/auth/refresh`, { - refreshToken: this.userAuth.refreshToken, - }); - - const { accessToken, expiresIn } = response.data; + const { accessToken, expiresIn } = await this.refreshToken(); const tokenExpirationTimestamp = now.getTime() + diff --git a/src/preload/index.ts b/src/preload/index.ts index 316397d22..24e6cf397 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -14,6 +14,7 @@ import type { CatalogueSearchPayload, SeedingStatus, GameAchievement, + ManageAccountPage, } from "@types"; import type { CatalogueCategory } from "@shared"; import type { AxiosProgressEvent } from "axios"; @@ -226,6 +227,8 @@ contextBridge.exposeInMainWorld("electron", { isPortableVersion: () => ipcRenderer.invoke("isPortableVersion"), openExternal: (src: string) => ipcRenderer.invoke("openExternal", src), openCheckout: () => ipcRenderer.invoke("openCheckout"), + openManageAccount: (page: ManageAccountPage) => + ipcRenderer.invoke("openManageAccount", page), showOpenDialog: (options: Electron.OpenDialogOptions) => ipcRenderer.invoke("showOpenDialog", options), showItemInFolder: (path: string) => diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 88f3297f1..33fcb7da2 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -29,6 +29,7 @@ import type { UserAchievement, ComparedAchievements, CatalogueSearchPayload, + ManageAccountPage, } from "@types"; import type { AxiosProgressEvent } from "axios"; import type disk from "diskusage"; @@ -187,6 +188,7 @@ declare global { /* Misc */ openExternal: (src: string) => Promise; openCheckout: () => Promise; + openManageAccount: (page: ManageAccountPage) => Promise; getVersion: () => Promise; isStaging: () => Promise; ping: () => string; diff --git a/src/renderer/src/pages/settings/settings-privacy.css.ts b/src/renderer/src/pages/settings/settings-account.css.ts similarity index 100% rename from src/renderer/src/pages/settings/settings-privacy.css.ts rename to src/renderer/src/pages/settings/settings-account.css.ts diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx new file mode 100644 index 000000000..16ed40e43 --- /dev/null +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -0,0 +1,217 @@ +import { Button, SelectField } from "@renderer/components"; +import { SPACING_UNIT, vars } 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"; + +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(); + + 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 ( +
+ { + const handleChange = ( + event: React.ChangeEvent + ) => { + field.onChange(event); + handleSubmit(onSubmit)(); + }; + + return ( + <> + ({ + key: visiblity.value, + value: visiblity.value, + label: visiblity.label, + }))} + disabled={isSubmitting} + /> + + {t("profile_visibility_description")} + + ); + }} + /> + +

+ {t("manage_account")} +

+ + {userDetails?.email ? ( +
+

{t("current_email")}

+

{userDetails.email}

+
+ ) : ( +

{t("no_associated_email")}

+ )} + +
+ + + +
+ +

+ {t("subscription")} +

+ {userDetails?.subscription?.expiresAt ? ( +

+ {t("subscription_active_until", { + date: formatDate(userDetails?.subscription?.expiresAt), + })} +

+ ) : ( +

{t("subscription_not_active")}

+ )} + +
+ +
+ +

+ {t("blocked_users")} +

+ +
    + {blockedUsers.length > 0 ? ( + blockedUsers.map((user) => { + return ( +
  • +
    + {user.displayName} + {user.displayName} +
    + + +
  • + ); + }) + ) : ( + {t("no_users_blocked")} + )} +
+ + ); +} diff --git a/src/renderer/src/pages/settings/settings-privacy.tsx b/src/renderer/src/pages/settings/settings-privacy.tsx deleted file mode 100644 index b93d1d07d..000000000 --- a/src/renderer/src/pages/settings/settings-privacy.tsx +++ /dev/null @@ -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(); - - 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 ( -
- { - const handleChange = ( - event: React.ChangeEvent - ) => { - field.onChange(event); - handleSubmit(onSubmit)(); - }; - - return ( - <> - ({ - key: visiblity.value, - value: visiblity.value, - label: visiblity.label, - }))} - disabled={isSubmitting} - /> - - {t("profile_visibility_description")} - - ); - }} - /> - -

- {t("blocked_users")} -

- -
    - {blockedUsers.map((user) => { - return ( -
  • -
    - {user.displayName} - {user.displayName} -
    - - -
  • - ); - })} -
- - ); -} diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx index dffdfbaeb..5fba6c5df 100644 --- a/src/renderer/src/pages/settings/settings.tsx +++ b/src/renderer/src/pages/settings/settings.tsx @@ -11,7 +11,7 @@ import { SettingsContextConsumer, SettingsContextProvider, } from "@renderer/context"; -import { SettingsPrivacy } from "./settings-privacy"; +import { SettingsAccount } from "./settings-account"; import { useUserDetails } from "@renderer/hooks"; import { useMemo } from "react"; @@ -28,7 +28,7 @@ export default function Settings() { "Real-Debrid", ]; - if (userDetails) return [...categories, t("privacy")]; + if (userDetails) return [...categories, t("account")]; return categories; }, [userDetails, t]); @@ -53,7 +53,7 @@ export default function Settings() { return ; } - return ; + return ; }; return ( diff --git a/src/types/index.ts b/src/types/index.ts index 345893a5d..66c458b5d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -416,6 +416,8 @@ export interface CatalogueSearchPayload { developers: string[]; } +export type ManageAccountPage = "update-email" | "update-password"; + export * from "./steam.types"; export * from "./real-debrid.types"; export * from "./ludusavi.types"; From c4378c0ffc44af29a78d123e56559c34b3c3dd8c Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:26:44 -0300 Subject: [PATCH 13/36] feat: update user details on settings account tab --- src/main/services/hosters/datanodes.ts | 3 ++- .../src/pages/settings/settings-account.tsx | 20 +++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/main/services/hosters/datanodes.ts b/src/main/services/hosters/datanodes.ts index d77e7d514..ae1444180 100644 --- a/src/main/services/hosters/datanodes.ts +++ b/src/main/services/hosters/datanodes.ts @@ -33,7 +33,8 @@ export class DatanodesApi { "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, } ); diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 16ed40e43..14fc35640 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -1,5 +1,5 @@ import { Button, SelectField } from "@renderer/components"; -import { SPACING_UNIT, vars } from "@renderer/theme.css"; +import { SPACING_UNIT } from "@renderer/theme.css"; import { Controller, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; @@ -36,9 +36,13 @@ export function SettingsAccount() { handleSubmit, } = useForm(); - const { patchUser, userDetails } = useUserDetails(); - - const { unblockUser } = useUserDetails(); + const { + userDetails, + patchUser, + fetchUserDetails, + updateUserDetails, + unblockUser, + } = useUserDetails(); useEffect(() => { if (userDetails?.profileVisibility) { @@ -46,6 +50,14 @@ export function SettingsAccount() { } }, [userDetails, setValue]); + useEffect(() => { + fetchUserDetails().then((response) => { + if (response) { + updateUserDetails(response); + } + }); + }, []); + const visibilityOptions = [ { value: "PUBLIC", label: t("public") }, { value: "FRIENDS", label: t("friends_only") }, From ffd3e37b484994a80de531ce499cb927ba68a5ec Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 16:57:25 -0300 Subject: [PATCH 14/36] feat: refactor --- src/main/services/hydra-api.ts | 56 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 4ab20e801..16bbc21ff 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -216,41 +216,41 @@ export class HydraApi { } public static async refreshToken() { - return this.instance + const { accessToken, expiresIn } = await this.instance .post<{ accessToken: string; expiresIn: number }>(`/auth/refresh`, { refreshToken: this.userAuth.refreshToken, }) .then((response) => response.data); + + const tokenExpirationTimestamp = + Date.now() + + this.secondsToMilliseconds(expiresIn) - + this.EXPIRATION_OFFSET_IN_MS; + + this.userAuth.authToken = accessToken; + this.userAuth.expirationTimestamp = tokenExpirationTimestamp; + + logger.log( + "Token refreshed. New expiration:", + this.userAuth.expirationTimestamp + ); + + userAuthRepository.upsert( + { + id: 1, + accessToken, + tokenExpirationTimestamp, + }, + ["id"] + ); + + return { accessToken, expiresIn }; } private static async revalidateAccessTokenIfExpired() { - const now = new Date(); - - if (this.userAuth.expirationTimestamp < now.getTime()) { + if (this.userAuth.expirationTimestamp < Date.now()) { try { - const { accessToken, expiresIn } = await this.refreshToken(); - - const tokenExpirationTimestamp = - now.getTime() + - this.secondsToMilliseconds(expiresIn) - - this.EXPIRATION_OFFSET_IN_MS; - - this.userAuth.authToken = accessToken; - this.userAuth.expirationTimestamp = tokenExpirationTimestamp; - - logger.log( - "Token refreshed. New expiration:", - this.userAuth.expirationTimestamp - ); - - userAuthRepository.upsert( - { - id: 1, - accessToken, - tokenExpirationTimestamp, - }, - ["id"] - ); + await this.refreshToken(); } catch (err) { this.handleUnauthorizedError(err); } @@ -265,7 +265,7 @@ export class HydraApi { }; } - private static handleUnauthorizedError = (err) => { + private static readonly handleUnauthorizedError = (err) => { if (err instanceof AxiosError && err.response?.status === 401) { logger.error( "401 - Current credentials:", From 56fabb288110e7809c3c0be1f75451006ea7fd36 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:08:07 -0300 Subject: [PATCH 15/36] fix: hook dependencies --- src/renderer/src/pages/settings/settings-account.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 14fc35640..89f5bde10 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -56,7 +56,7 @@ export function SettingsAccount() { updateUserDetails(response); } }); - }, []); + }, [fetchUserDetails, updateUserDetails]); const visibilityOptions = [ { value: "PUBLIC", label: t("public") }, From 15f721ac3935d1bfc0ae7c32b8f88e90fbafbcfa Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:11:02 -0300 Subject: [PATCH 16/36] feat: use Avatar component and remove non null assertion --- src/renderer/src/pages/settings/settings-account.css.ts | 7 ------- src/renderer/src/pages/settings/settings-account.tsx | 9 +++++---- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/src/renderer/src/pages/settings/settings-account.css.ts b/src/renderer/src/pages/settings/settings-account.css.ts index 2aec8cd07..44ec0d143 100644 --- a/src/renderer/src/pages/settings/settings-account.css.ts +++ b/src/renderer/src/pages/settings/settings-account.css.ts @@ -8,13 +8,6 @@ export const form = style({ gap: `${SPACING_UNIT}px`, }); -export const blockedUserAvatar = style({ - width: "32px", - height: "32px", - borderRadius: "4px", - filter: "grayscale(100%)", -}); - export const blockedUser = style({ display: "flex", minWidth: "240px", diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 89f5bde10..0aedb4b88 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -1,4 +1,4 @@ -import { Button, SelectField } from "@renderer/components"; +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"; @@ -201,10 +201,11 @@ export function SettingsAccount() { alignItems: "center", }} > - {user.displayName} {user.displayName} From 9941460c60afab8a9063c2cfcf55ce87cadd006d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 17:13:36 -0300 Subject: [PATCH 17/36] feat: code review --- src/renderer/src/pages/settings/settings-account.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 0aedb4b88..6ba4cb7a4 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -163,7 +163,7 @@ export function SettingsAccount() { {userDetails?.subscription?.expiresAt ? (

{t("subscription_active_until", { - date: formatDate(userDetails?.subscription?.expiresAt), + date: formatDate(userDetails.subscription.expiresAt), })}

) : ( From 44fd971c9554cdce4261098b66a98595f07a63e1 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Wed, 15 Jan 2025 23:56:37 -0300 Subject: [PATCH 18/36] feat: refactor open auth --- src/locales/en/translation.json | 2 +- src/locales/pt-BR/translation.json | 2 +- src/main/events/auth/open-auth-window.ts | 21 +++++++++++++--- src/main/events/index.ts | 1 - src/main/events/misc/open-manage-account.ts | 25 ------------------- src/main/services/window-manager.ts | 11 +++----- src/preload/index.ts | 8 +++--- .../components/sidebar/sidebar-profile.tsx | 5 ++-- src/renderer/src/declaration.d.ts | 6 ++--- .../game-details/game-details-content.tsx | 6 ++--- .../src/pages/settings/settings-account.tsx | 9 ++++--- src/shared/constants.ts | 6 +++++ src/types/index.ts | 2 -- 13 files changed, 47 insertions(+), 57 deletions(-) delete mode 100644 src/main/events/misc/open-manage-account.ts diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index e55954bff..a79b1107d 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -291,7 +291,7 @@ "update_email": "Update email", "update_password": "Update password", "current_email": "Current email:", - "no_associated_email": "You don't have an associated email yet" + "no_email_account": "You have not set an email yet" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 24ab46538..62565165e 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -279,7 +279,7 @@ "update_email": "Atualizar email", "update_password": "Atualizar senha", "current_email": "Email atual:", - "no_associated_email": "Você ainda não associou nenhum email a sua conta" + "no_email_account": "Você ainda não adicionou um email a sua conta" }, "notifications": { "download_complete": "Download concluído", diff --git a/src/main/events/auth/open-auth-window.ts b/src/main/events/auth/open-auth-window.ts index e93a5a42e..84b333d2b 100644 --- a/src/main/events/auth/open-auth-window.ts +++ b/src/main/events/auth/open-auth-window.ts @@ -1,7 +1,22 @@ +import i18next from "i18next"; 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) => - WindowManager.openAuthWindow(); +const openAuthWindow = async ( + _event: Electron.IpcMainInvokeEvent, + page: AuthPage +) => { + const searchParams = new URLSearchParams({ + lng: i18next.language, + }); + + if ([AuthPage.UpdateEmail, AuthPage.UpdatePassword].includes(page)) { + const { accessToken } = await HydraApi.refreshToken(); + searchParams.set("token", accessToken); + } + + return WindowManager.openAuthWindow(page, searchParams); +}; registerEvent("openAuthWindow", openAuthWindow); diff --git a/src/main/events/index.ts b/src/main/events/index.ts index f0f882cad..25882c3fc 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -30,7 +30,6 @@ import "./library/remove-game-from-library"; import "./library/select-game-wine-prefix"; import "./library/reset-game-achievements"; import "./misc/open-checkout"; -import "./misc/open-manage-account"; import "./misc/open-external"; import "./misc/show-open-dialog"; import "./misc/get-features"; diff --git a/src/main/events/misc/open-manage-account.ts b/src/main/events/misc/open-manage-account.ts deleted file mode 100644 index 2946b04b3..000000000 --- a/src/main/events/misc/open-manage-account.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { shell } from "electron"; -import { registerEvent } from "../register-event"; -import { HydraApi, logger } from "@main/services"; -import { ManageAccountPage } from "@types"; - -const openManageAccount = async ( - _event: Electron.IpcMainInvokeEvent, - page: ManageAccountPage -) => { - try { - const { accessToken } = await HydraApi.refreshToken(); - - const params = new URLSearchParams({ - token: accessToken, - }); - - shell.openExternal( - `${import.meta.env.MAIN_VITE_AUTH_URL}/${page}?${params.toString()}` - ); - } catch (err) { - logger.error("Failed to open manage account", err); - } -}; - -registerEvent("openManageAccount", openManageAccount); diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index a7cfcee2a..adf34a42b 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -9,7 +9,7 @@ import { shell, } from "electron"; import { is } from "@electron-toolkit/utils"; -import i18next, { t } from "i18next"; +import { t } from "i18next"; import path from "node:path"; import icon from "@resources/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 { HydraApi } from "./hydra-api"; import UserAgent from "user-agents"; +import { AuthPage } from "@shared"; export class WindowManager { 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) { const authWindow = new BrowserWindow({ width: 600, @@ -164,12 +165,8 @@ export class WindowManager { if (!app.isPackaged) authWindow.webContents.openDevTools(); - const searchParams = new URLSearchParams({ - lng: i18next.language, - }); - 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", () => { diff --git a/src/preload/index.ts b/src/preload/index.ts index 24e6cf397..07b4ec99b 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -14,9 +14,8 @@ import type { CatalogueSearchPayload, SeedingStatus, GameAchievement, - ManageAccountPage, } from "@types"; -import type { CatalogueCategory } from "@shared"; +import type { AuthPage, CatalogueCategory } from "@shared"; import type { AxiosProgressEvent } from "axios"; contextBridge.exposeInMainWorld("electron", { @@ -227,8 +226,6 @@ contextBridge.exposeInMainWorld("electron", { isPortableVersion: () => ipcRenderer.invoke("isPortableVersion"), openExternal: (src: string) => ipcRenderer.invoke("openExternal", src), openCheckout: () => ipcRenderer.invoke("openCheckout"), - openManageAccount: (page: ManageAccountPage) => - ipcRenderer.invoke("openManageAccount", page), showOpenDialog: (options: Electron.OpenDialogOptions) => ipcRenderer.invoke("showOpenDialog", options), showItemInFolder: (path: string) => @@ -294,7 +291,8 @@ contextBridge.exposeInMainWorld("electron", { /* Auth */ signOut: () => ipcRenderer.invoke("signOut"), - openAuthWindow: () => ipcRenderer.invoke("openAuthWindow"), + openAuthWindow: (page: AuthPage) => + ipcRenderer.invoke("openAuthWindow", page), getSessionHash: () => ipcRenderer.invoke("getSessionHash"), onSignIn: (cb: () => void) => { const listener = (_event: Electron.IpcRendererEvent) => cb(); diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 49e56ab78..3897ac545 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next"; import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; import SteamLogo from "@renderer/assets/steam-logo.svg?react"; import { Avatar } from "../avatar/avatar"; +import { AuthPage } from "@shared"; const LONG_POLLING_INTERVAL = 120_000; @@ -26,11 +27,11 @@ export function SidebarProfile() { const handleProfileClick = () => { if (userDetails === null) { - window.electron.openAuthWindow(); + window.electron.openAuthWindow(AuthPage.SignIn); return; } - navigate(`/profile/${userDetails!.id}`); + navigate(`/profile/${userDetails.id}`); }; useEffect(() => { diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 33fcb7da2..d2e50fde6 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -1,4 +1,4 @@ -import type { CatalogueCategory } from "@shared"; +import type { AuthPage, CatalogueCategory } from "@shared"; import type { AppUpdaterEvent, Game, @@ -29,7 +29,6 @@ import type { UserAchievement, ComparedAchievements, CatalogueSearchPayload, - ManageAccountPage, } from "@types"; import type { AxiosProgressEvent } from "axios"; import type disk from "diskusage"; @@ -188,7 +187,6 @@ declare global { /* Misc */ openExternal: (src: string) => Promise; openCheckout: () => Promise; - openManageAccount: (page: ManageAccountPage) => Promise; getVersion: () => Promise; isStaging: () => Promise; ping: () => string; @@ -210,7 +208,7 @@ declare global { /* Auth */ signOut: () => Promise; - openAuthWindow: () => Promise; + openAuthWindow: (page: AuthPage) => Promise; getSessionHash: () => Promise; onSignIn: (cb: () => void) => () => Electron.IpcRenderer; onSignOut: (cb: () => void) => () => Electron.IpcRenderer; diff --git a/src/renderer/src/pages/game-details/game-details-content.tsx b/src/renderer/src/pages/game-details/game-details-content.tsx index 12495231e..bd138e81c 100644 --- a/src/renderer/src/pages/game-details/game-details-content.tsx +++ b/src/renderer/src/pages/game-details/game-details-content.tsx @@ -10,7 +10,7 @@ import { Sidebar } from "./sidebar/sidebar"; import * as styles from "./game-details.css"; import { useTranslation } from "react-i18next"; 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 { useUserDetails } from "@renderer/hooks"; @@ -69,7 +69,7 @@ export function GameDetailsContent() { }); const backgroundColor = output - ? (new Color(output).darken(0.7).toString() as string) + ? new Color(output).darken(0.7).toString() : ""; setGameColor(backgroundColor); @@ -101,7 +101,7 @@ export function GameDetailsContent() { const handleCloudSaveButtonClick = () => { if (!userDetails) { - window.electron.openAuthWindow(); + window.electron.openAuthWindow(AuthPage.SignIn); return; } diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 6ba4cb7a4..4a19819d6 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -13,6 +13,7 @@ import { XCircleFillIcon, } from "@primer/octicons-react"; import { settingsContext } from "@renderer/context"; +import { AuthPage } from "@shared"; interface FormValues { profileVisibility: "PUBLIC" | "FRIENDS" | "PRIVATE"; @@ -128,7 +129,7 @@ export function SettingsAccount() {

{userDetails.email}

) : ( -

{t("no_associated_email")}

+

{t("no_email_account")}

)}
-
+ {getHydraCloudSectionContent().callToAction} + +

{t("blocked_users")} From ff0ef740664a7cd06363018a606a27e6ef7677ff Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:38:28 -0300 Subject: [PATCH 21/36] feat: update icon order --- src/renderer/src/pages/settings/settings-account.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 79303703f..76feb9571 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -174,8 +174,8 @@ export function SettingsAccount() { theme="outline" onClick={() => window.electron.openAuthWindow(AuthPage.UpdateEmail)} > - {t("update_email")} + {t("update_email")} @@ -199,8 +199,8 @@ export function SettingsAccount() { theme="outline" onClick={() => window.electron.openCheckout()} > - {getHydraCloudSectionContent().callToAction} + {getHydraCloudSectionContent().callToAction}

From 153ab051747367eb7743e8549a7a2b6a503262cb Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:47:24 -0300 Subject: [PATCH 22/36] feat: remove unused string --- src/locales/en/translation.json | 1 - src/locales/pt-BR/translation.json | 1 - 2 files changed, 2 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index e894727c7..30913b2b4 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -284,7 +284,6 @@ "account": "Account", "no_users_blocked": "You have no blocked users", "subscription_active_until": "Enjoy your Hydra Cloud until {{date}}", - "subscription_not_active": "You don't have an active Hydra Cloud subscription", "manage_subscription": "Manage subscription", "update_email": "Update email", "update_password": "Update password", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 8f22f5c24..70052f28f 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -272,7 +272,6 @@ "account": "Conta", "no_users_blocked": "Você não bloqueou nenhum usuário", "subscription_active_until": "Aproveite seu Hydra Cloud até {{date}}", - "subscription_not_active": "Você não possui uma assinatura Hydra Cloud ativa", "manage_subscription": "Gerenciar assinatura", "update_email": "Atualizar email", "update_password": "Atualizar senha", From 923f7d7e806730c721269df1b32899565fd2f8ee Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 09:48:08 -0300 Subject: [PATCH 23/36] feat: review --- src/main/events/auth/open-auth-window.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/events/auth/open-auth-window.ts b/src/main/events/auth/open-auth-window.ts index 84b333d2b..925dbdd3d 100644 --- a/src/main/events/auth/open-auth-window.ts +++ b/src/main/events/auth/open-auth-window.ts @@ -16,7 +16,7 @@ const openAuthWindow = async ( searchParams.set("token", accessToken); } - return WindowManager.openAuthWindow(page, searchParams); + WindowManager.openAuthWindow(page, searchParams); }; registerEvent("openAuthWindow", openAuthWindow); From 81cb73c24333710277211895b54baa6e6314372d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:22:10 -0300 Subject: [PATCH 24/36] feat: code suggestion --- src/main/services/window-manager.ts | 2 +- src/shared/constants.ts | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 47fc92e0e..f7e82f072 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -166,7 +166,7 @@ export class WindowManager { if (!app.isPackaged) authWindow.webContents.openDevTools(); authWindow.loadURL( - `${import.meta.env.MAIN_VITE_AUTH_URL}/${page}?${searchParams.toString()}` + `${import.meta.env.MAIN_VITE_AUTH_URL}${page}?${searchParams.toString()}` ); authWindow.once("ready-to-show", () => { diff --git a/src/shared/constants.ts b/src/shared/constants.ts index 62a97d839..f2bcc7939 100644 --- a/src/shared/constants.ts +++ b/src/shared/constants.ts @@ -44,7 +44,7 @@ export enum Cracker { } export enum AuthPage { - SignIn = "", - UpdateEmail = "update-email", - UpdatePassword = "update-password", + SignIn = "/", + UpdateEmail = "/update-email", + UpdatePassword = "/update-password", } From 2346a5bf86d16d19477a2d4f788c45623f394d3b Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:32:59 -0300 Subject: [PATCH 25/36] feat: add renewal info --- src/locales/en/translation.json | 5 ++- src/locales/pt-BR/translation.json | 7 +++- .../src/pages/settings/settings-account.tsx | 39 ++++++++++++++----- 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 30913b2b4..55eac4389 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -293,7 +293,10 @@ "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" + "become_subscriber": "Be Hydra Cloud", + "subscription_renew_cancelled": "Automatic renewal is not active", + "subscription_renews_on": "Your subscription renews on {{date}}", + "bill_sent_until": "You next bill will be sent until this day" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 70052f28f..13c75e4d0 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -280,8 +280,11 @@ "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 ao seu máximo", - "become_subscriber": "Seja Hydra Cloud" + "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": { "download_complete": "Download concluído", diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index 76feb9571..6357a2431 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -96,27 +96,48 @@ export function SettingsAccount() { const getHydraCloudSectionContent = () => { const hasSubscribedBefore = Boolean(userDetails?.subscription?.expiresAt); + const isRenewalActive = userDetails?.subscription?.status === "active"; if (!hasSubscribedBefore) { return { - description: t("no_subscription"), + description: {t("no_subscription")}, callToAction: t("become_subscriber"), }; } if (hasActiveSubscription) { return { - description: t("subscription_active_until", { - date: formatDate(userDetails!.subscription!.expiresAt!), - }), + description: isRenewalActive ? ( + <> + + {t("subscription_renews_on", { + date: formatDate(userDetails.subscription!.expiresAt!), + })} + + {t("bill_sent_until")} + + ) : ( + <> + {t("subscription_renew_cancelled")} + + {t("subscription_active_until", { + date: formatDate(userDetails!.subscription!.expiresAt!), + })} + + + ), callToAction: t("manage_subscription"), }; } return { - description: t("subscription_expired_at", { - date: formatDate(userDetails!.subscription!.expiresAt!), - }), + description: ( + + {t("subscription_expired_at", { + date: formatDate(userDetails!.subscription!.expiresAt!), + })} + + ), callToAction: t("renew_subscription"), }; }; @@ -189,9 +210,9 @@ export function SettingsAccount() { -
+

Hydra Cloud

- {getHydraCloudSectionContent().description} + {getHydraCloudSectionContent().description}
-
-

Hydra Cloud

+
+

Hydra Cloud

{getHydraCloudSectionContent().description}
From b06339d362815d6d04c712abd2cc7ffd4f1de867 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:55:13 -0300 Subject: [PATCH 27/36] feat: handle refreshToken failure --- src/main/events/auth/open-auth-window.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/events/auth/open-auth-window.ts b/src/main/events/auth/open-auth-window.ts index 925dbdd3d..0f5ec3718 100644 --- a/src/main/events/auth/open-auth-window.ts +++ b/src/main/events/auth/open-auth-window.ts @@ -12,7 +12,9 @@ const openAuthWindow = async ( }); if ([AuthPage.UpdateEmail, AuthPage.UpdatePassword].includes(page)) { - const { accessToken } = await HydraApi.refreshToken(); + const { accessToken } = await HydraApi.refreshToken().catch(() => { + return { accessToken: "" }; + }); searchParams.set("token", accessToken); } From 5d0e8258808b45f6c8bd49666bbab5c2aea70df3 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:57:28 -0300 Subject: [PATCH 28/36] fix: i18n --- src/locales/en/translation.json | 2 +- src/locales/pt-BR/translation.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 0288cdf77..1baf46141 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -296,7 +296,7 @@ "become_subscriber": "Be Hydra Cloud", "subscription_renew_cancelled": "Automatic renewal is disabled", "subscription_renews_on": "Your subscription renews on {{date}}", - "bill_sent_until": "You next bill will be sent until this day" + "bill_sent_until": "Your next bill will be sent until this day" }, "notifications": { "download_complete": "Download complete", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 23093f22e..ed891f04a 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -413,7 +413,7 @@ "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": "Está é uma conquista oculta", + "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:", From d1fa4895e46d3f3be21d73314918ab452a84bbc3 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Thu, 16 Jan 2025 12:58:09 -0300 Subject: [PATCH 29/36] feat: adjust spacing --- src/locales/pt-BR/translation.json | 2 +- .../pages/settings/settings-account.css.ts | 3 +- .../src/pages/settings/settings-account.tsx | 159 ++++++++++-------- 3 files changed, 89 insertions(+), 75 deletions(-) diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index ed891f04a..9e1021fca 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -271,7 +271,7 @@ "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 estará ativa até {{date}}", + "subscription_active_until": "Sua assinatura Hydra Cloud ficará ativa até {{date}}", "manage_subscription": "Gerenciar assinatura", "update_email": "Atualizar email", "update_password": "Atualizar senha", diff --git a/src/renderer/src/pages/settings/settings-account.css.ts b/src/renderer/src/pages/settings/settings-account.css.ts index 44ec0d143..8fbb38456 100644 --- a/src/renderer/src/pages/settings/settings-account.css.ts +++ b/src/renderer/src/pages/settings/settings-account.css.ts @@ -5,7 +5,7 @@ import { SPACING_UNIT, vars } from "../../theme.css"; export const form = style({ display: "flex", flexDirection: "column", - gap: `${SPACING_UNIT}px`, + gap: `${SPACING_UNIT * 3}px`, }); export const blockedUser = style({ @@ -36,5 +36,4 @@ export const blockedUsersList = style({ flexDirection: "column", alignItems: "flex-start", gap: `${SPACING_UNIT}px`, - marginTop: `${SPACING_UNIT}px`, }); diff --git a/src/renderer/src/pages/settings/settings-account.tsx b/src/renderer/src/pages/settings/settings-account.tsx index aa8a75968..d43e5b2dc 100644 --- a/src/renderer/src/pages/settings/settings-account.tsx +++ b/src/renderer/src/pages/settings/settings-account.tsx @@ -158,7 +158,7 @@ export function SettingsAccount() { }; return ( - <> +
{t("profile_visibility_description")} - +
); }} /> -
+

{t("current_email")}

{userDetails?.email ?? t("no_email_account")}

-
-
+ + + +
+ + +
- + {getHydraCloudSectionContent().description} +
- + -
-

Hydra Cloud

- {getHydraCloudSectionContent().description} -
- - - -

- {t("blocked_users")} -

+

{t("blocked_users")}

-
    {blockedUsers.length > 0 ? ( - blockedUsers.map((user) => { - return ( -
  • -
    - - {user.displayName} -
    - - -
  • - ); - }) +
      + {blockedUsers.map((user) => { + return ( +
    • +
      + + {user.displayName} +
      + + +
    • + ); + })} +
    ) : ( {t("no_users_blocked")} )} -
+ ); } From 8ebb5edfbc61d172ddd39207c9961f84cef73d83 Mon Sep 17 00:00:00 2001 From: hydrasources Date: Thu, 16 Jan 2025 21:29:28 +0300 Subject: [PATCH 30/36] Update translation.json --- src/locales/ru/translation.json | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 92008a5e0..f96ef4957 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -278,7 +278,23 @@ "source_already_exists": "Этот источник уже добавлен", "user_unblocked": "Пользователь разблокирован", "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": { "download_complete": "Загрузка завершена", From 2ba653429fadc7455538cf7fc0196c13701bfa0f Mon Sep 17 00:00:00 2001 From: vitorRibeiro7 Date: Thu, 16 Jan 2025 18:20:08 -0300 Subject: [PATCH 31/36] fix: fix css bug on requirements details style --- src/renderer/src/pages/game-details/sidebar/sidebar.css.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts index aa27cd421..8242e7c92 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts @@ -34,6 +34,7 @@ export const requirementButton = style({ }); export const requirementsDetails = style({ + minHeight: "500px", padding: `${SPACING_UNIT * 2}px`, lineHeight: "22px", fontSize: "16px", From 049c27cdb7d8e7e104a722327a220e8e785633bf Mon Sep 17 00:00:00 2001 From: vitorRibeiro7 Date: Fri, 17 Jan 2025 11:32:28 -0300 Subject: [PATCH 32/36] fix; revert minHeight --- src/renderer/src/pages/game-details/sidebar/sidebar.css.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts index 8242e7c92..aa27cd421 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.css.ts @@ -34,7 +34,6 @@ export const requirementButton = style({ }); export const requirementsDetails = style({ - minHeight: "500px", padding: `${SPACING_UNIT * 2}px`, lineHeight: "22px", fontSize: "16px", From 4e34f41ee0d60a1c2138475a8cc36028131dad13 Mon Sep 17 00:00:00 2001 From: vitorRibeiro7 Date: Fri, 17 Jan 2025 11:33:44 -0300 Subject: [PATCH 33/36] ench: remove maxHeight on sidebar section --- .../src/pages/game-details/sidebar-section/sidebar-section.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx index da9d078f0..adbc59301 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx @@ -26,8 +26,6 @@ export function SidebarSection({ title, children }: SidebarSectionProps) {
Date: Fri, 17 Jan 2025 11:54:14 -0300 Subject: [PATCH 34/36] fix: reset overflow hidden --- .../src/pages/game-details/sidebar-section/sidebar-section.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx index adbc59301..4858cc0a9 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx @@ -26,6 +26,7 @@ export function SidebarSection({ title, children }: SidebarSectionProps) {
Date: Fri, 17 Jan 2025 11:55:03 -0300 Subject: [PATCH 35/36] ench: add dynamic height --- .../game-details/sidebar-section/sidebar-section.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx index 4858cc0a9..4b01adeb7 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx @@ -1,5 +1,5 @@ import { ChevronDownIcon } from "@primer/octicons-react"; -import { useRef, useState } from "react"; +import { useEffect, useRef, useState } from "react"; import * as styles from "./sidebar-section.css"; @@ -11,6 +11,13 @@ export interface SidebarSectionProps { export function SidebarSection({ title, children }: SidebarSectionProps) { const content = useRef(null); const [isOpen, setIsOpen] = useState(true); + const [height, setHeight] = useState(0); + + useEffect(() => { + if (content.current) { + setHeight(isOpen ? content.current.scrollHeight : 0); + } + }, [isOpen, children]); return (
@@ -26,6 +33,7 @@ export function SidebarSection({ title, children }: SidebarSectionProps) {
Date: Fri, 17 Jan 2025 12:25:35 -0300 Subject: [PATCH 36/36] refactor: add non re-render rules to useEffect --- .../pages/game-details/sidebar-section/sidebar-section.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx index 4b01adeb7..e24f677be 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.tsx @@ -14,10 +14,12 @@ export function SidebarSection({ title, children }: SidebarSectionProps) { const [height, setHeight] = useState(0); useEffect(() => { - if (content.current) { + if (content.current && content.current.scrollHeight !== height) { setHeight(isOpen ? content.current.scrollHeight : 0); + } else if (!isOpen) { + setHeight(0); } - }, [isOpen, children]); + }, [isOpen, children, height]); return (