Compare commits

..

No commits in common. "main" and "v3.2.0" have entirely different histories.
main ... v3.2.0

27 changed files with 226 additions and 377 deletions

View file

@ -1,9 +1,5 @@
name: Build
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on: pull_request
jobs:

View file

@ -1,9 +1,5 @@
name: Lint
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on: pull_request
jobs:

View file

@ -1,9 +1,5 @@
name: Release
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
push:
branches: main

View file

@ -1,6 +1,6 @@
{
"name": "hydralauncher",
"version": "3.2.3",
"version": "3.2.0",
"description": "Hydra",
"main": "./out/main/index.js",
"author": "Los Broxas",
@ -71,7 +71,6 @@
"react-loading-skeleton": "^3.4.0",
"react-redux": "^9.1.1",
"react-router-dom": "^6.22.3",
"react-tooltip": "^5.28.0",
"sound-play": "^1.1.0",
"sudo-prompt": "^9.2.1",
"tar": "^7.4.3",

View file

@ -7,18 +7,18 @@
"featured": "مميز",
"surprise_me": "مفاجئني",
"no_results": "لم يتم العثور على نتائج",
"start_typing": "ابدأ بالكتابة للبحث...",
"hot": "الأكثر شهرة الآن",
"start_typing": "ابدأ الكتابة للبحث...",
"hot": "الأكثر شيوعًا الآن",
"weekly": "📅 أفضل ألعاب الأسبوع",
"achievements": "🏆 ألعاب يجب إكمالها"
"achievements": "🏆 ألعاب للتغلب عليها"
},
"sidebar": {
"catalogue": "الفهرس",
"catalogue": "الكـتالوج",
"downloads": "التنزيلات",
"settings": "الإعدادات",
"my_library": "مكتبتي",
"downloading_metadata": "{{title}} (جاري تنزيل البيانات الوصفية...)",
"paused": "{{title}} (معلق)",
"downloading_metadata": "{{title}} (جارٍ تنزيل البيانات الوصفية...)",
"paused": "{{title}} (معلّق)",
"downloading": "{{title}} ({{percentage}} - جاري التنزيل...)",
"filter": "تصفية المكتبة",
"home": "الرئيسية",
@ -26,13 +26,12 @@
"game_has_no_executable": "اللعبة لا تحتوي على ملف تشغيل",
"sign_in": "تسجيل الدخول",
"friends": "الأصدقاء",
"need_help": "تحتاج مساعدة؟",
"favorites": "المفضلة"
"need_help": "تحتاج مساعدة؟"
},
"header": {
"search": "بحث الألعاب",
"search": "ابحث عن الألعاب",
"home": "الرئيسية",
"catalogue": "الفهرس",
"catalogue": "الكـتالوج",
"downloads": "التنزيلات",
"search_results": "نتائج البحث",
"settings": "الإعدادات",
@ -41,16 +40,16 @@
},
"bottom_panel": {
"no_downloads_in_progress": "لا توجد تنزيلات قيد التقدم",
"downloading_metadata": "جاري تنزيل بيانات {{title}} الوصفية...",
"downloading": "جاري تنزيل {{title}}... ({{percentage}} مكتمل) - الوقت المتبقي {{eta}} - السرعة {{speed}}",
"calculating_eta": "جاري تنزيل {{title}}... ({{percentage}} مكتمل) - جاري حساب الوقت المتبقي...",
"checking_files": "جاري فحص ملفات {{title}}... ({{percentage}} مكتمل)"
"downloading_metadata": "جارٍ تنزيل البيانات الوصفية لـ {{title}}...",
"downloading": "جارٍ تنزيل {{title}}... ({{percentage}} اكتمال) - الوقت المتبقي {{eta}} - السرعة {{speed}}",
"calculating_eta": "جارٍ تنزيل {{title}}... ({{percentage}} اكتمال) - جاري حساب الوقت المتبقي...",
"checking_files": "جارٍ فحص ملفات {{title}}... ({{percentage}} اكتمال)"
},
"catalogue": {
"search": "تصفية...",
"developers": "المطورون",
"genres": "الأنواع",
"tags": "الوسوم",
"tags": "العلامات",
"publishers": "الناشرون",
"download_sources": "مصادر التنزيل",
"result_count": "{{resultCount}} نتيجة",
@ -69,34 +68,34 @@
"cancel": "إلغاء",
"remove": "إزالة",
"space_left_on_disk": "{{space}} متبقي على القرص",
"eta": "الانتهاء المتوقع {{eta}}",
"calculating_eta": "جاري حساب الوقت المتبقي...",
"downloading_metadata": "جاري تنزيل البيانات الوصفية...",
"filter": "تصفية الإصدارات المعادة",
"eta": "الانتهاء {{eta}}",
"calculating_eta": "جارٍ حساب الوقت المتبقي...",
"downloading_metadata": "جارٍ تنزيل البيانات الوصفية...",
"filter": "تصفية الحزم المعاد تعبئتها",
"requirements": "متطلبات النظام",
"minimum": "الحد الأدنى",
"recommended": ستحسن",
"paused": "معلق",
"recommended": ُوصى به",
"paused": "معلّق",
"release_date": "تاريخ الإصدار {{date}}",
"publisher": "نشر بواسطة {{publisher}}",
"hours": "ساعات",
"minutes": "دقائق",
"amount_hours": "{{amount}} ساعة",
"amount_minutes": "{{amount}} دقيقة",
"amount_hours": "{{amount}} ساعات",
"amount_minutes": "{{amount}} دقائق",
"accuracy": "دقة {{accuracy}}%",
"add_to_library": "إضافة إلى المكتبة",
"remove_from_library": "إزالة من المكتبة",
"no_downloads": "لا توجد تنزيلات متاحة",
"play_time": "وقت اللعب {{amount}}",
"last_time_played": "آخر مرة لعب {{period}}",
"play_time": "لعب لمدة {{amount}}",
"last_time_played": "آخر تشغيل {{period}}",
"not_played_yet": "لم تلعب {{title}} بعد",
"next_suggestion": "الاقتراح التالي",
"play": "تشغيل",
"deleting": "جاري حذف المثبت...",
"deleting": "جارٍ حذف المثبت...",
"close": "إغلاق",
"playing_now": "جاري التشغيل الآن",
"playing_now": "يتم التشغيل الآن",
"change": "تغيير",
"repacks_modal_description": "اختر الإصدار المعاد الذي تريد تنزيله",
"repacks_modal_description": "اختر الحزمة المعاد تعبئتها التي تريد تنزيلها",
"select_folder_hint": "لتغيير المجلد الافتراضي، انتقل إلى <0>الإعدادات</0>",
"download_now": "تنزيل الآن",
"no_shop_details": "تعذر الحصول على تفاصيل المتجر.",
@ -111,12 +110,12 @@
"select_executable": "تحديد",
"no_executable_selected": "لم يتم تحديد ملف تشغيل",
"open_folder": "فتح المجلد",
"open_download_location": "عرض الملفات المنزلة",
"open_download_location": "عرض الملفات المحملة",
"create_shortcut": "إنشاء اختصار على سطح المكتب",
"clear": "مسح",
"remove_files": "إزالة الملفات",
"remove_from_library_title": "هل أنت متأكد؟",
"remove_from_library_description": "سيتم إزالة {{game}} من مكتبتك",
"remove_from_library_description": "سيؤدي هذا إلى إزالة {{game}} من مكتبتك",
"options": "خيارات",
"executable_section_title": "ملف التشغيل",
"executable_section_description": "مسار الملف الذي سيتم تشغيله عند النقر على \"تشغيل\"",
@ -124,35 +123,35 @@
"downloads_section_description": "تحقق من التحديثات أو الإصدارات الأخرى لهذه اللعبة",
"danger_zone_section_title": "منطقة الخطر",
"danger_zone_section_description": "إزالة هذه اللعبة من مكتبتك أو الملفات التي تم تنزيلها بواسطة Hydra",
"download_in_progress": "جاري التنزيل",
"download_in_progress": "تنزيل قيد التقدم",
"download_paused": "التنزيل معلق",
"last_downloaded_option": "خيار التنزيل الأخير",
"create_shortcut_success": "تم إنشاء الاختصار بنجاح",
"create_shortcut_error": "خطأ في إنشاء الاختصار",
"nsfw_content_title": "هذه اللعبة تحتوي على محتوى غير لائق",
"nsfw_content_description": "{{title}} يحتوي على محتوى قد لا يكون مناسبًا لجميع الأعمار. هل تريد المتابعة؟",
"nsfw_content_description": "{{title}} يحتوي على محتوى قد لا يناسب جميع الأعمار. هل تريد المتابعة؟",
"allow_nsfw_content": "متابعة",
"refuse_nsfw_content": "رجوع",
"stats": "الإحصائيات",
"download_count": "التنزيلات",
"download_count": "مرات التنزيل",
"player_count": "اللاعبون النشطون",
"download_error": "خيار التنزيل هذا غير متاح",
"download": "تنزيل",
"executable_path_in_use": "مسار التشغيل مستخدم بالفعل بواسطة \"{{game}}\"",
"warning": "تحذير:",
"hydra_needs_to_remain_open": "لهذا التنزيل، يجب أن يظل Hydra مفتوحًا حتى اكتماله. إذا تم إغلاق Hydra قبل الاكتمال، ستفقد تقدمك.",
"hydra_needs_to_remain_open": "لهذا التنزيل، يجب أن يبقى Hydra مفتوحًا حتى اكتماله. إذا أغلق Hydra قبل الاكتمال، ستفقد تقدمك.",
"achievements": "الإنجازات",
"achievements_count": "الإنجازات {{unlockedCount}}/{{achievementsCount}}",
"cloud_save": "حفظ سحابي",
"cloud_save_description": "احفظ تقدمك في السحابة واستمر في اللعب من أي جهاز",
"cloud_save_description": "احفظ تقدمك على السحابة واستمر في اللعب من أي جهاز",
"backups": "النسخ الاحتياطية",
"install_backup": "تثبيت",
"delete_backup": "حذف",
"create_backup": "نسخة احتياطية جديدة",
"last_backup_date": "آخر نسخة احتياطية في {{date}}",
"no_backup_preview": "لم يتم العثور على حفظات لهذا العنوان",
"restoring_backup": "جاري استعادة النسخة الاحتياطية ({{progress}} مكتمل)...",
"uploading_backup": "جاري رفع النسخة الاحتياطية...",
"restoring_backup": "جارٍ استعادة النسخة الاحتياطية ({{progress}} اكتمال)...",
"uploading_backup": "جارٍ رفع النسخة الاحتياطية...",
"no_backups": "لم تقم بإنشاء أي نسخ احتياطية لهذه اللعبة بعد",
"backup_uploaded": "تم رفع النسخة الاحتياطية",
"backup_deleted": "تم حذف النسخة الاحتياطية",
@ -165,67 +164,61 @@
"files_automatically_mapped": "تم تعيين الملفات تلقائيًا",
"no_backups_created": "لم يتم إنشاء نسخ احتياطية لهذه اللعبة",
"manage_files": "إدارة الملفات",
"loading_save_preview": "جاري البحث عن حفظات اللعبة...",
"loading_save_preview": "جارٍ البحث عن حفظات الألعاب...",
"wine_prefix": "بادئة Wine",
"wine_prefix_description": "بادئة Wine المستخدمة لتشغيل هذه اللعبة",
"launch_options": "خيارات التشغيل",
"launch_options_description": "يمكن للمستخدمين المتقدمين إدخال تعديلات على خيارات التشغيل (ميزة تجريبية)",
"launch_options_placeholder": ا توجد معلمات محددة",
"launch_options_placeholder": م يتم تحديد أي معاملات",
"no_download_option_info": "لا توجد معلومات متاحة",
"backup_deletion_failed": "فشل في حذف النسخة الاحتياطية",
"max_number_of_artifacts_reached": "تم الوصول إلى الحد الأقصى من النسخ الاحتياطية لهذه اللعبة",
"achievements_not_sync": "شاهد كيفية مزامنة إنجازاتك",
"backup_deletion_failed": "فشل حذف النسخة الاحتياطية",
"max_number_of_artifacts_reached": "تم الوصول إلى الحد الأقصى لعدد النسخ الاحتياطية لهذه اللعبة",
"achievements_not_sync": "تعرف على كيفية مزامنة إنجازاتك",
"manage_files_description": "إدارة الملفات التي سيتم نسخها احتياطيًا واستعادتها",
"select_folder": "حدد المجلد",
"backup_from": "نسخة احتياطية من {{date}}",
"custom_backup_location_set": "تم تعيين موقع نسخ احتياطي مخصص",
"no_directory_selected": "لم يتم تحديد مجلد",
"no_write_permission": "لا يمكن التنزيل إلى هذا المجلد. انقر هنا للمزيد من المعلومات.",
"no_write_permission": "لا يمكن التنزيل إلى هذا المجلد. انقر هنا لمعرفة المزيد.",
"reset_achievements": "إعادة تعيين الإنجازات",
"reset_achievements_description": "سيؤدي هذا إلى إعادة تعيين جميع إنجازات {{game}}",
"reset_achievements_title": "هل أنت متأكد؟",
"reset_achievements_success": "تم إعادة تعيين الإنجازات بنجاح",
"reset_achievements_error": "فشل في إعادة تعيين الإنجازات",
"download_error_gofile_quota_exceeded": "لقد تجاوزت الحصة الشهرية لـ Gofile. يرجى الانتظار حتى إعادة تعيين الحصة.",
"download_error_real_debrid_account_not_authorized": "حساب Real-Debrid الخاص بك غير مصرح له بإجراء تنزيلات جديدة. يرجى مراجعة إعدادات الحساب والمحاولة مرة أخرى.",
"download_error_not_cached_in_real_debrid": "هذا التنزيل غير متوفر على Real-Debrid وجلب حالة التنزيل من Real-Debrid غير متاح حاليًا.",
"download_error_not_cached_in_torbox": "هذا التنزيل غير متوفر على Torbox وجلب حالة التنزيل من Torbox غير متاح حاليًا.",
"game_removed_from_favorites": "تمت إزالة اللعبة من المفضلة",
"game_added_to_favorites": "تمت إضافة اللعبة إلى المفضلة"
"reset_achievements_error": "فشل إعادة تعيين الإنجازات"
},
"activation": {
"title": "تفعيل Hydra",
"installation_id": "معرف التثبيت:",
"enter_activation_code": "أدخل رمز التفعيل الخاص بك",
"message": "إذا كنت لا تعرف أين تطلب هذا، فأنت لا يجب أن يكون لديك هذا.",
"message": "إذا كنت لا تعرف أين تطلب هذا، فلا يجب أن يكون لديك هذا.",
"activate": "تفعيل",
"loading": "جاري التحميل..."
"loading": "جارٍ التحميل..."
},
"downloads": {
"resume": "استئناف",
"pause": "إيقاف مؤقت",
"eta": "الانتهاء المتوقع {{eta}}",
"paused": "معلق",
"verifying": "جاري التحقق...",
"eta": "الانتهاء {{eta}}",
"paused": "معلّق",
"verifying": "جارٍ التحقق...",
"completed": "مكتمل",
"removed": "غير منزّل",
"removed": "غير محمل",
"cancel": "إلغاء",
"filter": "تصفية الألعاب المنزلة",
"filter": "تصفية الألعاب المحملة",
"remove": "إزالة",
"downloading_metadata": "جاري تنزيل البيانات الوصفية...",
"deleting": "جاري حذف المثبت...",
"delete": "حذف المثبت",
"downloading_metadata": "جارٍ تنزيل البيانات الوصفية...",
"deleting": "جارٍ حذف المثبت...",
"delete": "إزالة المثبت",
"delete_modal_title": "هل أنت متأكد؟",
"delete_modal_description": "سيؤدي هذا إلى إزالة جميع ملفات التثبيت من جهازك",
"install": "تثبيت",
"download_in_progress": "قيد التقدم",
"queued_downloads": "التنزيلات في قائمة الانتظار",
"downloads_completed": "مكتملة",
"downloads_completed": "مكتمل",
"queued": "في قائمة الانتظار",
"no_downloads_title": "لا شيء هنا",
"no_downloads_title": "فارغ جدًا",
"no_downloads_description": "لم تقم بتنزيل أي شيء باستخدام Hydra بعد، ولكن لم يفت الأوان للبدء.",
"checking_files": "جاري فحص الملفات...",
"seeding": "جاري التوزيع",
"checking_files": "جارٍ فحص الملفات...",
"seeding": "التوزيع",
"stop_seeding": "إيقاف التوزيع",
"resume_seeding": "استئناف التوزيع",
"options": "إدارة"
@ -235,8 +228,8 @@
"change": "تحديث",
"notifications": "الإشعارات",
"enable_download_notifications": "عند اكتمال التنزيل",
"enable_repack_list_notifications": "عند إضافة إصدار معاد جديد",
"real_debrid_api_token_label": "رمز Real-Debrid API",
"enable_repack_list_notifications": "عند إضافة حزمة معاد تعبئتها جديدة",
"real_debrid_api_token_label": "رمز واجهة برمجة تطبيقات Real-Debrid",
"quit_app_instead_hiding": "لا تخفي Hydra عند الإغلاق",
"launch_with_system": "تشغيل Hydra مع بدء النظام",
"general": "عام",
@ -245,21 +238,21 @@
"language": "اللغة",
"api_token": "رمز API",
"enable_real_debrid": "تفعيل Real-Debrid",
"real_debrid_description": "Real-Debrid هو أداة تنزيل غير مقيدة تتيح لك تنزيل الملفات بسرعة، محدودة فقط بسرعة اتصالك بالإنترنت.",
"real_debrid_description": "Real-Debrid هو أداة تنزيل غير مقيدة تتيح لك تنزيل الملفات بسرعة، مقيدة فقط بسرعة الإنترنت لديك.",
"debrid_invalid_token": "رمز API غير صالح",
"debrid_api_token_hint": "يمكنك الحصول على رمز API الخاص بك <0>هنا</0>",
"real_debrid_free_account_error": "الحساب \"{{username}}\" حساب مجاني. يرجى الاشتراك في Real-Debrid",
"real_debrid_free_account_error": "الحساب \"{{username}}\" هو حساب مجاني. يرجى الاشتراك في Real-Debrid",
"debrid_linked_message": "تم ربط الحساب \"{{username}}\"",
"save_changes": "حفظ التغييرات",
"changes_saved": "تم حفظ التغييرات بنجاح",
"download_sources_description": "سيقوم Hydra بجلب روابط التنزيل من هذه المصادر. يجب أن يكون عنوان URL للمصدر رابطًا مباشرًا لملف .json يحتوي على روابط التنزيل.",
"download_sources_description": "سيقوم Hydra بجلب روابط التنزيل من هذه المصادر. يجب أن يكون عنوان URL المصدر رابطًا مباشرًا لملف .json يحتوي على روابط التنزيل.",
"validate_download_source": "تحقق",
"remove_download_source": "إزالة",
"add_download_source": "إضافة مصدر",
"download_count_zero": "لا توجد خيارات تنزيل",
"download_count_one": "{{countFormatted}} خيار تنزيل",
"download_count_other": "{{countFormatted}} خيارات تنزيل",
"download_source_url": "عنوان مصدر التنزيل",
"download_source_url": "عنوان URL لمصدر التنزيل",
"add_download_source_description": "أدخل عنوان URL لملف .json",
"download_source_up_to_date": "محدث",
"download_source_errored": "خطأ",
@ -279,13 +272,13 @@
"profile_visibility": "رؤية الملف الشخصي",
"profile_visibility_description": "اختر من يمكنه رؤية ملفك الشخصي ومكتبتك",
"required_field": "هذا الحقل مطلوب",
"source_already_exists": "هذا المصدر مضاف مسبقًا",
"must_be_valid_url": "يجب أن يكون المصدر عنوان URL صالح",
"source_already_exists": "تمت إضافة هذا المصدر مسبقًا",
"must_be_valid_url": "يجب أن يكون المصدر عنوان URL صالحًا",
"blocked_users": "المستخدمون المحظورون",
"user_unblocked": "تم إلغاء حظر المستخدم",
"enable_achievement_notifications": "عند فتح إنجاز",
"launch_minimized": "تشغيل Hydra مصغرًا",
"disable_nsfw_alert": "تعطيل تنبيهات المحتوى غير اللائق",
"disable_nsfw_alert": "تعطيل تنبيه المحتوى غير اللائق",
"seed_after_download_complete": "التوزيع بعد اكتمال التنزيل",
"show_hidden_achievement_description": "عرض وصف الإنجازات المخفية قبل فتحها",
"account": "الحساب",
@ -303,47 +296,18 @@
"become_subscriber": "كن مشتركًا في Hydra Cloud",
"subscription_renew_cancelled": "تم تعطيل التجديد التلقائي",
"subscription_renews_on": "سيتم تجديد اشتراكك في {{date}}",
"bill_sent_until": "سيتم إرسال فاتورتك القادمة حتى هذا اليوم",
"no_themes": "يبدو أنه ليس لديك أي سمات بعد، لكن لا تقلق، انقر هنا لإنشاء أول تحفة فنية لك.",
"editor_tab_code": "الكود",
"editor_tab_info": "معلومات",
"editor_tab_save": "حفظ",
"web_store": "المتجر الإلكتروني",
"clear_themes": "مسح",
"create_theme": "إنشاء",
"create_theme_modal_title": "إنشاء سمة مخصصة",
"create_theme_modal_description": "إنشاء سمة جديدة لتخصيص مظهر Hydra",
"theme_name": "الاسم",
"insert_theme_name": "أدخل اسم السمة",
"set_theme": "تعيين السمة",
"unset_theme": "إلغاء تعيين السمة",
"delete_theme": "حذف السمة",
"edit_theme": "تعديل السمة",
"delete_all_themes": "حذف جميع السمات",
"delete_all_themes_description": "سيؤدي هذا إلى حذف جميع السمات المخصصة الخاصة بك",
"delete_theme_description": "سيؤدي هذا إلى حذف السمة {{theme}}",
"cancel": "إلغاء",
"appearance": "المظهر",
"enable_torbox": "تفعيل Torbox",
"torbox_description": "TorBox هي خدمة seedbox متميزة تنافس أفضل الخوادم في السوق.",
"torbox_account_linked": "تم ربط حساب TorBox",
"real_debrid_account_linked": "تم ربط حساب Real-Debrid",
"name_min_length": "يجب أن يكون اسم السمة على الأقل 3 أحرف",
"import_theme": "استيراد سمة",
"import_theme_description": "ستقوم باستيراد {{theme}} من متجر السمات",
"error_importing_theme": "خطأ في استيراد السمة",
"theme_imported": "تم استيراد السمة بنجاح"
"bill_sent_until": "سيتم إرسال فاتورتك التالية حتى هذا اليوم"
},
"notifications": {
"download_complete": "اكتمل التنزيل",
"game_ready_to_install": "{{title}} جاهز للتثبيت",
"repack_list_updated": "تم تحديث قائمة الإصدارات المعادة",
"repack_count_one": "تمت إضافة {{count}} إصدار معاد",
"repack_count_other": "تمت إضافة {{count}} إصدارات معادة",
"repack_list_updated": "تم تحديث قائمة الحزم المعاد تعبئتها",
"repack_count_one": "تمت إضافة {{count}} حزمة معاد تعبئتها",
"repack_count_other": "تمت إضافة {{count}} حزم معاد تعبئتها",
"new_update_available": "الإصدار {{version}} متوفر",
"restart_to_install_update": "أعد تشغيل Hydra لتثبيت التحديث",
"notification_achievement_unlocked_title": "تم فتح إنجاز لـ {{game}}",
"notification_achievement_unlocked_body": "{{achievement}} و {{count}} أخرى تم فتحها"
"notification_achievement_unlocked_body": "{{achievement}} و {{count}} آخرين تم فتحهم"
},
"system_tray": {
"open": "فتح Hydra",
@ -355,7 +319,7 @@
"binary_not_found_modal": {
"title": "البرامج غير مثبتة",
"description": "لم يتم العثور على ملفات تشغيل Wine أو Lutris على نظامك",
"instructions": "تحقق من الطريقة الصحيحة لتثبيت أي منها على توزيعة Linux الخاصة بك حتى تعمل اللعبة بشكل طبيعي"
"instructions": "تحقق من الطريقة الصحيحة لتثبيت أي منها على توزيعة لينكس الخاصة بك حتى تعمل اللعبة بشكل طبيعي"
},
"modal": {
"close": "زر الإغلاق"
@ -364,16 +328,16 @@
"toggle_password_visibility": "تبديل رؤية كلمة المرور"
},
"user_profile": {
"amount_hours": "{{amount}} ساعة",
"amount_minutes": "{{amount}} دقيقة",
"last_time_played": "آخر مرة لعب {{period}}",
"amount_hours": "{{amount}} ساعات",
"amount_minutes": "{{amount}} دقائق",
"last_time_played": "آخر تشغيل {{period}}",
"activity": "النشاط الأخير",
"library": "المكتبة",
"total_play_time": "إجمالي وقت اللعب",
"no_recent_activity_title": "لا شيء هنا...",
"no_recent_activity_title": "همم... لا شيء هنا",
"no_recent_activity_description": "لم تلعب أي ألعاب مؤخرًا. حان الوقت لتغيير ذلك!",
"display_name": "اسم العرض",
"saving": "جاري الحفظ",
"saving": "جارٍ الحفظ",
"save": "حفظ",
"edit_profile": "تعديل الملف الشخصي",
"saved_successfully": "تم الحفظ بنجاح",
@ -382,13 +346,13 @@
"cancel": "إلغاء",
"successfully_signed_out": "تم تسجيل الخروج بنجاح",
"sign_out": "تسجيل الخروج",
"playing_for": "جاري اللعب لمدة {{amount}}",
"sign_out_modal_text": "مكتبتك مرتبطة بحسابك الحالي. عند تسجيل الخروج، لن تكون مكتبتك مرئية، ولن يتم حفظ أي تقدم. هل تتابع تسجيل الخروج؟",
"playing_for": "يلعب لمدة {{amount}}",
"sign_out_modal_text": "مكتبتك مرتبطة بحسابك الحالي. عند تسجيل الخروج، لن تكون مكتبتك مرئية بعد الآن، ولن يتم حفظ أي تقدم. هل تتابع تسجيل الخروج؟",
"add_friends": "إضافة أصدقاء",
"add": "إضافة",
"friend_code": "رمز الصديق",
"see_profile": "عرض الملف الشخصي",
"sending": "جاري الإرسال",
"sending": "جارٍ الإرسال",
"friend_request_sent": "تم إرسال طلب الصداقة",
"friends": "الأصدقاء",
"friends_list": "قائمة الأصدقاء",
@ -407,19 +371,19 @@
"blocked_users": "المستخدمون المحظورون",
"unblock": "إلغاء الحظر",
"no_friends_added": "ليس لديك أصدقاء مضافون",
"pending": "معلق",
"pending": "قيد الانتظار",
"no_pending_invites": "ليس لديك دعوات معلقة",
"no_blocked_users": "ليس لديك مستخدمون محظورون",
"friend_code_copied": "تم نسخ رمز الصديق",
"undo_friendship_modal_text": "سيؤدي هذا إلى إلغاء صداقتك مع {{displayName}}",
"privacy_hint": "لضبط من يمكنه رؤية هذا، انتقل إلى <0>الإعدادات</0>",
"locked_profile": "هذا الملف الشخصي خاص",
"image_process_failure": "فشل في معالجة الصورة",
"image_process_failure": "فشل معالجة الصورة",
"required_field": "هذا الحقل مطلوب",
"displayname_min_length": "يجب أن يكون اسم العرض على الأقل 3 أحرف",
"displayname_max_length": "يجب أن لا يتجاوز اسم العرض 50 حرفًا",
"report_profile": "الإبلاغ عن هذا الملف",
"report_reason": "لماذا تقوم بالإبلاغ عن هذا الملف؟",
"displayname_max_length": "يجب ألا يتجاوز اسم العرض 50 حرفًا",
"report_profile": "الإبلاغ عن هذا الملف الشخصي",
"report_reason": "لماذا تقوم بالإبلاغ عن هذا الملف الشخصي؟",
"report_description": "معلومات إضافية",
"report_description_placeholder": "معلومات إضافية",
"report": "الإبلاغ",
@ -429,32 +393,32 @@
"report_reason_spam": "بريد عشوائي",
"report_reason_other": "أخرى",
"profile_reported": "تم الإبلاغ عن الملف الشخصي",
"your_friend_code": "رمز الصديق الخاص بك:",
"upload_banner": "رفع بانر",
"uploading_banner": "جاري رفع البانر...",
"your_friend_code": "رمز صديقك:",
"upload_banner": "تحميل بانر",
"uploading_banner": "جارٍ تحميل البانر...",
"background_image_updated": "تم تحديث صورة الخلفية",
"stats": "الإحصائيات",
"achievements": "الإنجازات",
"achievements": "إنجازات",
"games": "الألعاب",
"top_percentile": "الأعلى {{percentile}}%",
"top_percentile": "ال{{percentile}}% الأعلى",
"ranking_updated_weekly": "يتم تحديث التصنيف أسبوعيًا",
"playing": "جاري لعب {{game}}",
"playing": "يلعب {{game}}",
"achievements_unlocked": "الإنجازات المفتوحة",
"earned_points": "النقاط المكتسبة",
"show_achievements_on_profile": "عرض إنجازاتك في ملفك الشخصي",
"show_points_on_profile": "عرض نقاطك المكتسبة في ملفك الشخصي"
"show_achievements_on_profile": "عرض إنجازاتك على ملفك الشخصي",
"show_points_on_profile": "عرض نقاطك المكتسبة على ملفك الشخصي"
},
"achievement": {
"achievement_unlocked": "تم فتح الإنجاز",
"user_achievements": "إنجازات {{displayName}}",
"your_achievements": "إنجازاتك",
"unlocked_at": "تم الفتح في: {{date}}",
"subscription_needed": "يحتاج إلى اشتراك Hydra Cloud لعرض هذا المحتوى",
"subscription_needed": "يحتاج إلى اشتراك Hydra Cloud لرؤية هذا المحتوى",
"new_achievements_unlocked": "تم فتح {{achievementCount}} إنجازات جديدة من {{gameCount}} ألعاب",
"achievement_progress": "{{unlockedCount}}/{{totalCount}} إنجازات",
"achievements_unlocked_for_game": "تم فتح {{achievementCount}} إنجازات جديدة لـ {{gameTitle}}",
"hidden_achievement_tooltip": "هذا إنجاز مخفي",
"achievement_earn_points": "احصل على {{points}} نقاط مع هذا الإنجاز",
"achievement_earn_points": "اكسب {{points}} نقطة مع هذا الإنجاز",
"earned_points": "النقاط المكتسبة:",
"available_points": "النقاط المتاحة:",
"how_to_earn_achievements_points": "كيفية كسب نقاط الإنجازات؟"
@ -464,10 +428,10 @@
"subscribe_now": "اشترك الآن",
"cloud_saving": "حفظ سحابي",
"cloud_achievements": "احفظ إنجازاتك على السحابة",
"animated_profile_picture": "صورة ملف متحركة",
"animated_profile_picture": "صورة ملف شخصي متحركة",
"premium_support": "دعم ممتاز",
"show_and_compare_achievements": "اعرض وقارن إنجازاتك مع المستخدمين الآخرين",
"animated_profile_banner": "بانر ملف متحرك",
"animated_profile_banner": "بانر ملف شخصي متحرك",
"hydra_cloud": "Hydra Cloud",
"hydra_cloud_feature_found": "لقد اكتشفت ميزة Hydra Cloud!",
"learn_more": "معرفة المزيد"

View file

@ -183,13 +183,7 @@
"no_write_permission": "Невозможно загрузить в эту директорию. Нажмите здесь, чтобы узнать больше.",
"reset_achievements_title": "Вы уверены?",
"reset_achievements_success": "Достижения успешно сброшены",
"reset_achievements_error": "Не удалось сбросить достижения",
"download_error_gofile_quota_exceeded": "Вы превысили месячную квоту Gofile. Пожалуйста, подождите, пока квота не будет восстановлена.",
"download_error_real_debrid_account_not_authorized": "Ваш аккаунт Real-Debrid не авторизован для осуществления новых загрузок. Пожалуйста, проверьте настройки учетной записи и повторите попытку.",
"download_error_not_cached_in_real_debrid": "Эта загрузка недоступна на Real-Debrid, а опрос статуса загрузки с Real-Debrid пока недоступен.",
"download_error_not_cached_in_torbox": "Эта загрузка недоступна на Torbox, и опросить статус загрузки с Torbox пока невозможно.",
"game_added_to_favorites": "Игра добавлена в избранное",
"game_removed_from_favorites": "Игра удалена из избранного"
"reset_achievements_error": "Не удалось сбросить достижения"
},
"activation": {
"title": "Активировать Hydra",
@ -301,36 +295,7 @@
"become_subscriber": "Станьте обладателем Hydra Cloud",
"subscription_renew_cancelled": "Автоматическое продление отключено",
"subscription_renews_on": "Ваша подписка продлевается на {{date}}",
"bill_sent_until": "Ваш следующий счет будет отправлен до этого дня",
"no_themes": "Похоже, что у вас еще нет тем, но не волнуйтесь, нажмите здесь, чтобы создать свой первый шедевр",
"editor_tab_code": "Код",
"editor_tab_info": "Информация",
"editor_tab_save": "Сохранить",
"web_store": "Веб-магазин",
"clear_themes": "Очистить",
"create_theme": "Создать",
"create_theme_modal_title": "Создать пользовательскую тему",
"create_theme_modal_description": "Создать новую тему для настройки внешнего вида Hydra",
"theme_name": "Название",
"insert_theme_name": "Вставить название темы",
"set_theme": "Установить тему",
"unset_theme": "Снять тему",
"delete_theme": "Удалить тему",
"edit_theme": "Редактировать тему",
"delete_all_themes": "Удалить все темы",
"delete_all_themes_description": "Это удалит все ваши пользовательские темы",
"delete_theme_description": "Это приведет к удалению темы {{theme}}",
"cancel": "Отменить",
"appearance": "Внешний вид",
"enable_torbox": "Включить Torbox",
"torbox_description": "TorBox - это ваш премиум-сервис, конкурирующий даже с лучшими серверами на рынке.",
"torbox_account_linked": "Аккаунт TorBox привязан",
"real_debrid_account_linked": "Аккаунт Real-Debrid привязан",
"name_min_length": "Название темы должно содержать не менее 3 символов",
"import_theme": "Импортировать тему",
"import_theme_description": "Вы импортируете {{theme}} из магазина тем",
"error_importing_theme": "Ошибка при импорте темы",
"theme_imported": "Тема успешно импортирована"
"bill_sent_until": "Ваш следующий счет будет отправлен до этого дня"
},
"notifications": {
"download_complete": "Загрузка завершена",

View file

@ -87,7 +87,6 @@ import "./themes/get-custom-theme-by-id";
import "./themes/get-active-custom-theme";
import "./themes/close-editor-window";
import "./themes/toggle-custom-theme";
import "./misc/get-badges";
import { isPortableVersion } from "@main/helpers";
ipcMain.handle("ping", () => "pong");

View file

@ -1,22 +0,0 @@
import { Badge } from "@types";
import { registerEvent } from "../register-event";
import { HydraApi } from "@main/services";
import { db, levelKeys } from "@main/level";
const getBadges = async (_event: Electron.IpcMainInvokeEvent) => {
const language = await db
.get<string, string>(levelKeys.language, {
valueEncoding: "utf-8",
})
.then((language) => language || "en");
const params = new URLSearchParams({
locale: language,
});
return HydraApi.get<Badge[]>(`/badges?${params.toString()}`, null, {
needsAuth: false,
});
};
registerEvent("getBadges", getBadges);

View file

@ -132,9 +132,7 @@ const migrateFromSqlite = async () => {
);
if (rest.language) {
await db.put<string, string>(levelKeys.language, rest.language, {
valueEncoding: "utf-8",
});
await db.put(levelKeys.language, rest.language);
}
}
})

View file

@ -57,7 +57,7 @@ export class WindowManager {
trafficLightPosition: { x: 16, y: 16 },
titleBarOverlay: {
symbolColor: "#DADBE1",
color: "#00000000",
color: "#151515",
height: 34,
},
webPreferences: {

View file

@ -266,7 +266,6 @@ contextBridge.exposeInMainWorld("electron", {
showItemInFolder: (path: string) =>
ipcRenderer.invoke("showItemInFolder", path),
getFeatures: () => ipcRenderer.invoke("getFeatures"),
getBadges: () => ipcRenderer.invoke("getBadges"),
platform: process.platform,
/* Auto update */

View file

@ -263,7 +263,9 @@ export function App() {
useEffect(() => {
const unsubscribe = window.electron.onCssInjected((cssString) => {
injectCustomCss(cssString);
if (cssString) {
injectCustomCss(cssString);
}
});
return () => unsubscribe();

View file

@ -10,7 +10,6 @@
cursor: pointer;
color: globals.$muted-color;
position: relative;
overflow: hidden;
&__image {
height: 100%;

View file

@ -1,5 +1,4 @@
import { PersonIcon } from "@primer/octicons-react";
import cn from "classnames";
import "./avatar.scss";
@ -15,18 +14,11 @@ export interface AvatarProps
src?: string | null;
}
export function Avatar({ size, alt, src, className, ...props }: AvatarProps) {
export function Avatar({ size, alt, src, ...props }: AvatarProps) {
return (
<div className="profile-avatar" style={{ width: size, height: size }}>
{src ? (
<img
className={cn("profile-avatar__image", className)}
alt={alt}
src={src}
width={size}
height={size}
{...props}
/>
<img className="profile-avatar__image" alt={alt} src={src} {...props} />
) : (
<PersonIcon size={size * 0.7} />
)}

View file

@ -6,10 +6,7 @@ export interface BackdropProps {
children: React.ReactNode;
}
export function Backdrop({
isClosing = false,
children,
}: Readonly<BackdropProps>) {
export function Backdrop({ isClosing = false, children }: BackdropProps) {
return (
<div
className={cn("backdrop", {

View file

@ -15,7 +15,7 @@ export function Button({
theme = "primary",
className,
...props
}: Readonly<ButtonProps>) {
}: ButtonProps) {
return (
<button
type="button"

View file

@ -1,6 +1,6 @@
import { darkenColor } from "@renderer/helpers";
import { useAppSelector, useToast } from "@renderer/hooks";
import type { Badge, UserProfile, UserStats } from "@types";
import type { UserProfile, UserStats } from "@types";
import { average } from "color.js";
import { createContext, useCallback, useEffect, useState } from "react";
@ -16,7 +16,6 @@ export interface UserProfileContext {
getUserProfile: () => Promise<void>;
setSelectedBackgroundImage: React.Dispatch<React.SetStateAction<string>>;
backgroundImage: string;
badges: Badge[];
}
export const DEFAULT_USER_PROFILE_BACKGROUND = "#151515B3";
@ -29,7 +28,6 @@ export const userProfileContext = createContext<UserProfileContext>({
getUserProfile: async () => {},
setSelectedBackgroundImage: () => {},
backgroundImage: "",
badges: [],
});
const { Provider } = userProfileContext;
@ -43,13 +41,12 @@ export interface UserProfileContextProviderProps {
export function UserProfileContextProvider({
children,
userId,
}: Readonly<UserProfileContextProviderProps>) {
}: UserProfileContextProviderProps) {
const { userDetails } = useAppSelector((state) => state.userDetails);
const [userStats, setUserStats] = useState<UserStats | null>(null);
const [userProfile, setUserProfile] = useState<UserProfile | null>(null);
const [badges, setBadges] = useState<Badge[]>([]);
const [heroBackground, setHeroBackground] = useState(
DEFAULT_USER_PROFILE_BACKGROUND
);
@ -104,18 +101,12 @@ export function UserProfileContextProvider({
});
}, [navigate, getUserStats, showErrorToast, userId, t]);
const getBadges = useCallback(async () => {
const badges = await window.electron.getBadges();
setBadges(badges);
}, []);
useEffect(() => {
setUserProfile(null);
setHeroBackground(DEFAULT_USER_PROFILE_BACKGROUND);
getUserProfile();
getBadges();
}, [getUserProfile, getBadges]);
}, [getUserProfile]);
return (
<Provider
@ -127,7 +118,6 @@ export function UserProfileContextProvider({
setSelectedBackgroundImage,
backgroundImage: getBackgroundImageUrl(),
userStats,
badges,
}}
>
{children}

View file

@ -30,7 +30,6 @@ import type {
GameRunning,
TorBoxUser,
Theme,
Badge,
} from "@types";
import type { AxiosProgressEvent } from "axios";
import type disk from "diskusage";
@ -218,7 +217,6 @@ declare global {
) => Promise<Electron.OpenDialogReturnValue>;
showItemInFolder: (path: string) => Promise<void>;
getFeatures: () => Promise<string[]>;
getBadges: () => Promise<Badge[]>;
platform: NodeJS.Platform;
/* Auto update */

View file

@ -11,7 +11,6 @@ import "@fontsource/noto-sans/500.css";
import "@fontsource/noto-sans/700.css";
import "react-loading-skeleton/dist/skeleton.css";
import "react-tooltip/dist/react-tooltip.css";
import { App } from "./app";
@ -19,17 +18,26 @@ import { store } from "./store";
import resources from "@locales";
import { SuspenseWrapper } from "./components";
import { logger } from "./logger";
import { addCookieInterceptor } from "./cookies";
const Home = React.lazy(() => import("./pages/home/home"));
const GameDetails = React.lazy(
() => import("./pages/game-details/game-details")
);
const Downloads = React.lazy(() => import("./pages/downloads/downloads"));
const Settings = React.lazy(() => import("./pages/settings/settings"));
const Catalogue = React.lazy(() => import("./pages/catalogue/catalogue"));
const Profile = React.lazy(() => import("./pages/profile/profile"));
const Achievements = React.lazy(
() => import("./pages/achievements/achievements")
);
const ThemeEditor = React.lazy(
() => import("./pages/theme-editor/theme-editor")
);
import * as Sentry from "@sentry/react";
import Catalogue from "./pages/catalogue/catalogue";
import Home from "./pages/home/home";
import Downloads from "./pages/downloads/downloads";
import GameDetails from "./pages/game-details/game-details";
import Settings from "./pages/settings/settings";
import Profile from "./pages/profile/profile";
import Achievements from "./pages/achievements/achievements";
import ThemeEditor from "./pages/theme-editor/theme-editor";
Sentry.init({
dsn: import.meta.env.RENDERER_VITE_SENTRY_DSN,
@ -74,16 +82,37 @@ ReactDOM.createRoot(document.getElementById("root")!).render(
<HashRouter>
<Routes>
<Route element={<App />}>
<Route path="/" element={<Home />} />
<Route path="/catalogue" element={<Catalogue />} />
<Route path="/downloads" element={<Downloads />} />
<Route path="/game/:shop/:objectId" element={<GameDetails />} />
<Route path="/settings" element={<Settings />} />
<Route path="/profile/:userId" element={<Profile />} />
<Route path="/achievements" element={<Achievements />} />
<Route path="/" element={<SuspenseWrapper Component={Home} />} />
<Route
path="/catalogue"
element={<SuspenseWrapper Component={Catalogue} />}
/>
<Route
path="/downloads"
element={<SuspenseWrapper Component={Downloads} />}
/>
<Route
path="/game/:shop/:objectId"
element={<SuspenseWrapper Component={GameDetails} />}
/>
<Route
path="/settings"
element={<SuspenseWrapper Component={Settings} />}
/>
<Route
path="/profile/:userId"
element={<SuspenseWrapper Component={Profile} />}
/>
<Route
path="/achievements"
element={<SuspenseWrapper Component={Achievements} />}
/>
</Route>
<Route path="/theme-editor" element={<ThemeEditor />} />
<Route
path="/theme-editor"
element={<SuspenseWrapper Component={ThemeEditor} />}
/>
</Routes>
</HashRouter>
</Provider>

View file

@ -31,7 +31,7 @@ $logo-max-width: 200px;
display: flex;
justify-content: center;
width: 100%;
gap: calc(globals.$spacing-unit / 2);
gap: globals.$spacing-unit / 2;
color: globals.$body-color;
cursor: pointer;

View file

@ -16,8 +16,13 @@ import { useUserDetails } from "@renderer/hooks";
import { useSubscription } from "@renderer/hooks/use-subscription";
import "./game-details.scss";
const HERO_HEIGHT = 300;
const HERO_ANIMATION_THRESHOLD = 25;
export function GameDetailsContent() {
const heroRef = useRef<HTMLDivElement | null>(null);
const containerRef = useRef<HTMLDivElement | null>(null);
const [isHeaderStuck, setIsHeaderStuck] = useState(false);
const { t } = useTranslation("game_details");
@ -56,7 +61,7 @@ export function GameDetailsContent() {
return t("no_shop_details");
}, [shopDetails, t]);
const [backdropOpacity, setBackdropOpacity] = useState(1);
const [backdropOpactiy, setBackdropOpacity] = useState(1);
const handleHeroLoad = async () => {
const output = await average(steamUrlBuilder.libraryHero(objectId!), {
@ -75,6 +80,26 @@ export function GameDetailsContent() {
setBackdropOpacity(1);
}, [objectId]);
const onScroll: React.UIEventHandler<HTMLElement> = (event) => {
const heroHeight = heroRef.current?.clientHeight ?? HERO_HEIGHT;
const scrollY = (event.target as HTMLDivElement).scrollTop;
const opacity = Math.max(
0,
1 - scrollY / (heroHeight - HERO_ANIMATION_THRESHOLD)
);
if (scrollY >= heroHeight && !isHeaderStuck) {
setIsHeaderStuck(true);
}
if (scrollY <= heroHeight && isHeaderStuck) {
setIsHeaderStuck(false);
}
setBackdropOpacity(opacity);
};
const handleCloudSaveButtonClick = () => {
if (!userDetails) {
window.electron.openAuthWindow(AuthPage.SignIn);
@ -97,25 +122,31 @@ export function GameDetailsContent() {
<div
className={`game-details__wrapper ${hasNSFWContentBlocked ? "game-details__wrapper--blurred" : ""}`}
>
<section className="game-details__container">
<img
src={steamUrlBuilder.libraryHero(objectId!)}
className="game-details__hero-image"
alt={game?.title}
onLoad={handleHeroLoad}
/>
<section
ref={containerRef}
onScroll={onScroll}
className="game-details__container"
>
<div ref={heroRef} className="game-details__hero">
<img
src={steamUrlBuilder.libraryHero(objectId!)}
className="game-details__hero-image"
alt={game?.title}
onLoad={handleHeroLoad}
/>
<div
className="game-details__hero-backdrop"
style={{
backgroundColor: gameColor,
flex: 1,
opacity: Math.min(1, 1 - backdropOpactiy),
}}
/>
<div
className="game-details__hero-logo-backdrop"
style={{ opacity: backdropOpacity }}
style={{ opacity: backdropOpactiy }}
>
<div className="game-details__hero-content">
<img
@ -142,7 +173,7 @@ export function GameDetailsContent() {
</div>
</div>
<HeroPanel />
<HeroPanel isHeaderStuck={isHeaderStuck} />
<div className="game-details__description-container">
<div className="game-details__description-content">

View file

@ -9,7 +9,11 @@ import { HeroPanelPlaytime } from "./hero-panel-playtime";
import { gameDetailsContext } from "@renderer/context";
import "./hero-panel.scss";
export function HeroPanel() {
export interface HeroPanelProps {
isHeaderStuck: boolean;
}
export function HeroPanel({ isHeaderStuck }: HeroPanelProps) {
const { t } = useTranslation("game_details");
const { formatDate } = useDate();
@ -50,7 +54,10 @@ export function HeroPanel() {
game?.download?.status === "paused";
return (
<div style={{ backgroundColor: gameColor }} className="hero-panel">
<div
style={{ backgroundColor: gameColor }}
className={`hero-panel ${isHeaderStuck ? "hero-panel--stuck" : ""}`}
>
<div className="hero-panel__content">{getInfo()}</div>
<div className="hero-panel__actions">
<HeroPanelActions />

View file

@ -27,11 +27,6 @@
}
}
&__badges {
display: flex;
gap: calc(globals.$spacing-unit / 2);
}
&__user-information {
display: flex;
padding: calc(globals.$spacing-unit * 7) calc(globals.$spacing-unit * 3);
@ -70,12 +65,6 @@
overflow: hidden;
}
&__display-name-container {
display: flex;
gap: globals.$spacing-unit;
align-items: center;
}
&__display-name {
font-weight: bold;
overflow: hidden;

View file

@ -24,7 +24,6 @@ import type { FriendRequestAction } from "@types";
import { EditProfileModal } from "../edit-profile-modal/edit-profile-modal";
import Skeleton from "react-loading-skeleton";
import { UploadBackgroundImageButton } from "../upload-background-image-button/upload-background-image-button";
import { Tooltip } from "react-tooltip";
import "./profile-hero.scss";
type FriendAction =
@ -35,14 +34,8 @@ export function ProfileHero() {
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
const [isPerformingAction, setIsPerformingAction] = useState(false);
const {
isMe,
badges,
getUserProfile,
userProfile,
heroBackground,
backgroundImage,
} = useContext(userProfileContext);
const { isMe, getUserProfile, userProfile, heroBackground, backgroundImage } =
useContext(userProfileContext);
const {
signOut,
updateFriendRequestState,
@ -267,6 +260,14 @@ export function ProfileHero() {
return (
<>
{/* <ConfirmationModal
visible
title={t("sign_out_modal_title")}
descriptionText={t("sign_out_modal_text")}
confirmButtonLabel={t("sign_out")}
cancelButtonLabel={t("cancel")}
/> */}
<EditProfileModal
visible={showEditProfileModal}
onClose={() => setShowEditProfileModal(false)}
@ -306,34 +307,9 @@ export function ProfileHero() {
<div className="profile-hero__information">
{userProfile ? (
<div className="profile-hero__display-name-container">
<h2 className="profile-hero__display-name">
{userProfile?.displayName}
</h2>
<div className="profile-hero__badges">
{userProfile.badges.map((badgeName) => {
const badge = badges.find((b) => b.name === badgeName);
if (!badge) return null;
return (
<img
key={badge.name}
src={badge.badge.url}
alt={badge.name}
width={24}
height={24}
data-tooltip-place="top"
data-tooltip-content={badge.description}
data-tooltip-id="badge-name"
/>
);
})}
<Tooltip id="badge-name" />
</div>
</div>
<h2 className="profile-hero__display-name">
{userProfile?.displayName}
</h2>
) : (
<Skeleton width={150} height={28} />
)}

View file

@ -4,7 +4,6 @@ import { ThemeActions, ThemeCard, ThemePlaceholder } from "./index";
import type { Theme } from "@types";
import { ImportThemeModal } from "./modals/import-theme-modal";
import { settingsContext } from "@renderer/context";
import { useNavigate } from "react-router-dom";
interface SettingsAppearanceProps {
appearance: {
@ -25,10 +24,8 @@ export function SettingsAppearance({
authorId: string;
authorName: string;
} | null>(null);
const [hasShownModal, setHasShownModal] = useState(false);
const { clearTheme } = useContext(settingsContext);
const navigate = useNavigate();
const loadThemes = useCallback(async () => {
const themesList = await window.electron.getAllCustomThemes();
@ -48,37 +45,20 @@ export function SettingsAppearance({
}, [loadThemes]);
useEffect(() => {
if (
appearance.theme &&
appearance.authorId &&
appearance.authorName &&
!hasShownModal
) {
if (appearance.theme && appearance.authorId && appearance.authorName) {
setIsImportThemeModalVisible(true);
setImportTheme({
theme: appearance.theme,
authorId: appearance.authorId,
authorName: appearance.authorName,
});
setHasShownModal(true);
navigate("/settings", { replace: true });
clearTheme();
}
}, [
appearance.theme,
appearance.authorId,
appearance.authorName,
navigate,
hasShownModal,
clearTheme,
]);
}, [appearance.theme, appearance.authorId, appearance.authorName]);
const onThemeImported = useCallback(() => {
setIsImportThemeModalVisible(false);
setImportTheme(null);
loadThemes();
}, [loadThemes]);
}, [clearTheme, loadThemes]);
return (
<div className="settings-appearance">
@ -110,7 +90,6 @@ export function SettingsAppearance({
onClose={() => {
setIsImportThemeModalVisible(false);
clearTheme();
setHasShownModal(false);
}}
onThemeImported={onThemeImported}
themeName={importTheme.theme}

View file

@ -130,14 +130,6 @@ export interface UserProfileCurrentGame extends Omit<GameRunning, "objectId"> {
export type ProfileVisibility = "PUBLIC" | "PRIVATE" | "FRIENDS";
export interface Badge {
name: string;
description: string;
badge: {
url: string;
};
}
export interface UserDetails {
id: string;
username: string;
@ -172,7 +164,6 @@ export interface UserProfile {
quirks: {
backupsPerGameLimit: number;
};
badges: string[];
}
export interface UpdateProfileRequest {

View file

@ -1663,14 +1663,6 @@
"@floating-ui/core" "^1.6.0"
"@floating-ui/utils" "^0.2.8"
"@floating-ui/dom@^1.6.1":
version "1.6.13"
resolved "https://registry.yarnpkg.com/@floating-ui/dom/-/dom-1.6.13.tgz#a8a938532aea27a95121ec16e667a7cbe8c59e34"
integrity sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==
dependencies:
"@floating-ui/core" "^1.6.0"
"@floating-ui/utils" "^0.2.9"
"@floating-ui/react-dom@^2.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/@floating-ui/react-dom/-/react-dom-2.1.2.tgz#a1349bbf6a0e5cb5ded55d023766f20a4d439a31"
@ -1683,11 +1675,6 @@
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.8.tgz#21a907684723bbbaa5f0974cf7730bd797eb8e62"
integrity sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==
"@floating-ui/utils@^0.2.9":
version "0.2.9"
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.9.tgz#50dea3616bc8191fb8e112283b49eaff03e78429"
integrity sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==
"@fontsource/noto-sans@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@fontsource/noto-sans/-/noto-sans-5.1.0.tgz#54a5edd1b2b8c8e17bec6a85d4ee3a53b4b89c1f"
@ -4292,7 +4279,7 @@ classic-level@^2.0.0:
napi-macros "^2.2.2"
node-gyp-build "^4.3.0"
classnames@^2.2.1, classnames@^2.2.6, classnames@^2.3.0, classnames@^2.5.1:
classnames@^2.2.1, classnames@^2.2.6, classnames@^2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
@ -8106,14 +8093,6 @@ react-style-singleton@^2.2.1:
invariant "^2.2.4"
tslib "^2.0.0"
react-tooltip@^5.28.0:
version "5.28.0"
resolved "https://registry.yarnpkg.com/react-tooltip/-/react-tooltip-5.28.0.tgz#c7b5343ab2d740a428494a3d8315515af1f26f46"
integrity sha512-R5cO3JPPXk6FRbBHMO0rI9nkUG/JKfalBSQfZedZYzmqaZQgq7GLzF8vcCWx6IhUCKg0yPqJhXIzmIO5ff15xg==
dependencies:
"@floating-ui/dom" "^1.6.1"
classnames "^2.3.0"
react@^18.2.0:
version "18.3.1"
resolved "https://registry.yarnpkg.com/react/-/react-18.3.1.tgz#49ab892009c53933625bd16b2533fc754cab2891"