From 7e5cef6e44d21056856e7935e219184c7dc61610 Mon Sep 17 00:00:00 2001 From: Daniel Saraiva Date: Sun, 7 Sep 2025 12:36:50 -0300 Subject: [PATCH 01/27] feat: Added the game's supported languages. Added the game's lowest historical price and a link to see the price in various stores (keyshop and official store) --- src/locales/en/translation.json | 1118 +++++++++-------- src/locales/pt-BR/translation.json | 1104 ++++++++-------- .../sidebar/game-language-section.tsx | 55 + .../sidebar/game-prices-section.tsx | 62 + .../pages/game-details/sidebar/sidebar.scss | 413 +++--- .../pages/game-details/sidebar/sidebar.tsx | 541 ++++---- .../src/pages/settings/settings-general.tsx | 8 + src/types/level.types.ts | 1 + src/types/steam.types.ts | 1 + 9 files changed, 1742 insertions(+), 1561 deletions(-) mode change 100644 => 100755 src/locales/en/translation.json mode change 100644 => 100755 src/locales/pt-BR/translation.json create mode 100755 src/renderer/src/pages/game-details/sidebar/game-language-section.tsx create mode 100755 src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx mode change 100644 => 100755 src/renderer/src/pages/game-details/sidebar/sidebar.scss mode change 100644 => 100755 src/renderer/src/pages/game-details/sidebar/sidebar.tsx diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json old mode 100644 new mode 100755 index 96f72e1b..a2a2372b --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -1,552 +1,566 @@ -{ - "language_name": "English", - "app": { - "successfully_signed_in": "Successfully signed in" - }, - "home": { - "featured": "Featured", - "surprise_me": "Surprise me", - "no_results": "No results found", - "start_typing": "Starting typing to search...", - "hot": "Hot now", - "weekly": "📅 Top games of the week", - "achievements": "🏆 Games to beat" - }, - "sidebar": { - "catalogue": "Catalogue", - "downloads": "Downloads", - "settings": "Settings", - "my_library": "My library", - "downloading_metadata": "{{title}} (Downloading metadata…)", - "paused": "{{title}} (Paused)", - "downloading": "{{title}} ({{percentage}} - Downloading…)", - "filter": "Filter library", - "home": "Home", - "queued": "{{title}} (Queued)", - "game_has_no_executable": "Game has no executable selected", - "sign_in": "Sign in", - "friends": "Friends", - "need_help": "Need help?", - "favorites": "Favorites", - "playable_button_title": "Show only games you can play now" - }, - "header": { - "search": "Search games", - "home": "Home", - "catalogue": "Catalogue", - "downloads": "Downloads", - "search_results": "Search results", - "settings": "Settings", - "version_available_install": "Version {{version}} available. Click here to restart and install.", - "version_available_download": "Version {{version}} available. Click here to download." - }, - "bottom_panel": { - "no_downloads_in_progress": "No downloads in progress", - "downloading_metadata": "Downloading {{title}} metadata…", - "downloading": "Downloading {{title}}… ({{percentage}} complete) - Completion {{eta}} - {{speed}}", - "calculating_eta": "Downloading {{title}}… ({{percentage}} complete) - Calculating remaining time…", - "checking_files": "Checking {{title}} files… ({{percentage}} complete)", - "installing_common_redist": "{{log}}…", - "installation_complete": "Installation complete", - "installation_complete_message": "Common redistributables installed successfully" - }, - "catalogue": { - "search": "Filter…", - "developers": "Developers", - "genres": "Genres", - "tags": "Tags", - "publishers": "Publishers", - "download_sources": "Download sources", - "result_count": "{{resultCount}} results", - "filter_count": "{{filterCount}} available", - "clear_filters": "Clear {{filterCount}} selected" - }, - "game_details": { - "open_download_options": "Open download options", - "download_options_zero": "No download option", - "download_options_one": "{{count}} download option", - "download_options_other": "{{count}} download options", - "updated_at": "Updated {{updated_at}}", - "install": "Install", - "resume": "Resume", - "pause": "Pause", - "cancel": "Cancel", - "remove": "Remove", - "space_left_on_disk": "{{space}} left on disk", - "eta": "Conclusion {{eta}}", - "calculating_eta": "Calculating remaining time…", - "downloading_metadata": "Downloading metadata…", - "filter": "Filter repacks", - "requirements": "System requirements", - "minimum": "Minimum", - "recommended": "Recommended", - "paused": "Paused", - "release_date": "Released on {{date}}", - "publisher": "Published by {{publisher}}", - "hours": "hours", - "minutes": "minutes", - "amount_hours": "{{amount}} hours", - "amount_minutes": "{{amount}} minutes", - "accuracy": "{{accuracy}}% accuracy", - "add_to_library": "Add to library", - "already_in_library": "Already in library", - "remove_from_library": "Remove from library", - "no_downloads": "No downloads available", - "play_time": "Played for {{amount}}", - "last_time_played": "Last played {{period}}", - "not_played_yet": "You haven't played {{title}} yet", - "next_suggestion": "Next suggestion", - "play": "Play", - "deleting": "Deleting installer…", - "close": "Close", - "playing_now": "Playing now", - "change": "Change", - "repacks_modal_description": "Choose the repack you want to download", - "select_folder_hint": "To change the default folder, go to the <0>Settings", - "download_now": "Download now", - "no_shop_details": "Could not retrieve shop details.", - "download_options": "Download options", - "download_path": "Download path", - "previous_screenshot": "Previous screenshot", - "next_screenshot": "Next screenshot", - "screenshot": "Screenshot {{number}}", - "open_screenshot": "Open screenshot {{number}}", - "download_settings": "Download settings", - "downloader": "Downloader", - "select_executable": "Select", - "no_executable_selected": "No executable selected", - "open_folder": "Open folder", - "open_download_location": "See downloaded files", - "create_shortcut": "Create desktop shortcut", - "clear": "Clear", - "remove_files": "Remove files", - "remove_from_library_title": "Are you sure?", - "remove_from_library_description": "This will remove {{game}} from your library", - "options": "Options", - "executable_section_title": "Executable", - "executable_section_description": "Path of the file that will be executed when \"Play\" is clicked", - "downloads_section_title": "Downloads", - "downloads_section_description": "Check out updates or other versions of this game", - "danger_zone_section_title": "Danger zone", - "danger_zone_section_description": "Remove this game from your library or the files downloaded by Hydra", - "download_in_progress": "Download in progress", - "download_paused": "Download paused", - "last_downloaded_option": "Last downloaded option", - "create_steam_shortcut": "Create Steam shortcut", - "create_shortcut_success": "Shortcut created successfully", - "you_might_need_to_restart_steam": "You might need to restart Steam to see the changes", - "create_shortcut_error": "Error creating shortcut", - "nsfw_content_title": "This game contains inappropriate content", - "nsfw_content_description": "{{title}} contains content that may not be suitable for all ages. Are you sure you want to continue?", - "allow_nsfw_content": "Continue", - "refuse_nsfw_content": "Go back", - "stats": "Stats", - "download_count": "Downloads", - "player_count": "Active players", - "download_error": "This download option is not available", - "download": "Download", - "executable_path_in_use": "Executable already in use by \"{{game}}\"", - "warning": "Warning:", - "hydra_needs_to_remain_open": "for this download, Hydra needs to remain open util it's completed. If Hydra closes before completing, you will lose your progress.", - "achievements": "Achievements", - "achievements_count": "Achievements {{unlockedCount}}/{{achievementsCount}}", - "cloud_save": "Cloud save", - "cloud_save_description": "Save your progress in the cloud and continue playing on any device", - "backups": "Backups", - "install_backup": "Install", - "delete_backup": "Delete", - "create_backup": "New backup", - "last_backup_date": "Last backup on {{date}}", - "no_backup_preview": "No save games were found for this title", - "restoring_backup": "Restoring backup ({{progress}} complete)…", - "uploading_backup": "Uploading backup…", - "no_backups": "You haven't created any backups for this game yet", - "backup_uploaded": "Backup uploaded", - "backup_deleted": "Backup deleted", - "backup_restored": "Backup restored", - "see_all_achievements": "See all achievements", - "sign_in_to_see_achievements": "Sign in to see achievements", - "mapping_method_automatic": "Automatic", - "mapping_method_manual": "Manual", - "mapping_method_label": "Mapping method", - "files_automatically_mapped": "Files automatically mapped", - "no_backups_created": "No backups created for this game", - "manage_files": "Manage files", - "loading_save_preview": "Searching for save games…", - "wine_prefix": "Wine Prefix", - "wine_prefix_description": "The Wine prefix used to run this game", - "launch_options": "Launch Options", - "launch_options_description": "Advanced users may choose to enter modifications to their launch options (experimental feature)", - "launch_options_placeholder": "No parameter specified", - "no_download_option_info": "No information available", - "backup_deletion_failed": "Failed to delete backup", - "max_number_of_artifacts_reached": "Maximum number of backups reached for this game", - "achievements_not_sync": "See how to synchronize your achievements", - "manage_files_description": "Manage which files will be backed up and restored", - "select_folder": "Select folder", - "backup_from": "Backup from {{date}}", - "automatic_backup_from": "Automatic backup from {{date}}", - "enable_automatic_cloud_sync": "Enable automatic cloud sync", - "custom_backup_location_set": "Custom backup location set", - "no_directory_selected": "No directory selected", - "no_write_permission": "Cannot download into this directory. Click here to learn more.", - "reset_achievements": "Reset achievements", - "reset_achievements_description": "This will reset all achievements for {{game}}", - "reset_achievements_title": "Are you sure?", - "reset_achievements_success": "Achievements successfully reset", - "reset_achievements_error": "Failed to reset achievements", - "download_error_gofile_quota_exceeded": "You have exceeded your Gofile monthly quota. Please await the quota to reset.", - "download_error_real_debrid_account_not_authorized": "Your Real-Debrid account is not authorized to make new downloads. Please check your account settings and try again.", - "download_error_not_cached_on_real_debrid": "This download is not available on Real-Debrid and polling download status from Real-Debrid is not yet available.", - "download_error_not_cached_on_torbox": "This download is not available on TorBox and polling download status from TorBox is not yet available.", - "download_error_not_cached_on_hydra": "This download is not available on Nimbus.", - "game_removed_from_favorites": "Game removed from favorites", - "game_added_to_favorites": "Game added to favorites", - "automatically_extract_downloaded_files": "Automatically extract downloaded files", - "create_start_menu_shortcut": "Create Start Menu shortcut", - "invalid_wine_prefix_path": "Invalid Wine prefix path", - "invalid_wine_prefix_path_description": "The path to the Wine prefix is invalid. Please check the path and try again.", - "missing_wine_prefix": "Wine prefix is required to create a backup on Linux", - "artifact_renamed": "Backup renamed successfully", - "rename_artifact": "Rename Backup", - "rename_artifact_description": "Rename the backup to a more descriptive name", - "artifact_name_label": "Backup name", - "artifact_name_placeholder": "Enter a name for the backup", - "save_changes": "Save changes", - "required_field": "This field is required", - "max_length_field": "This field must be less than {{length}} characters", - "freeze_backup": "Pin it so it's not overwritten by automatic backups", - "unfreeze_backup": "Unpin it", - "backup_frozen": "Backup pinned", - "backup_unfrozen": "Backup unpinned", - "backup_freeze_failed": "Failed to freeze backup", - "backup_freeze_failed_description": "You must leave at least one free slot for automatic backups" - }, - "activation": { - "title": "Activate Hydra", - "installation_id": "Installation ID:", - "enter_activation_code": "Enter your activation code", - "message": "If you don't know where to ask for this, then you shouldn't have this.", - "activate": "Activate", - "loading": "Loading…" - }, - "downloads": { - "resume": "Resume", - "pause": "Pause", - "eta": "Conclusion {{eta}}", - "paused": "Paused", - "verifying": "Verifying…", - "completed": "Completed", - "removed": "Not downloaded", - "cancel": "Cancel", - "filter": "Filter downloaded games", - "remove": "Remove", - "downloading_metadata": "Downloading metadata…", - "deleting": "Deleting installer…", - "delete": "Remove installer", - "delete_modal_title": "Are you sure?", - "delete_modal_description": "This will remove all the installation files from your computer", - "install": "Install", - "download_in_progress": "In progress", - "queued_downloads": "Queued downloads", - "downloads_completed": "Completed", - "queued": "Queued", - "no_downloads_title": "Such empty", - "no_downloads_description": "You haven't downloaded anything with Hydra yet, but it's never too late to start.", - "checking_files": "Checking files…", - "seeding": "Seeding", - "stop_seeding": "Stop seeding", - "resume_seeding": "Resume seeding", - "options": "Manage", - "extract": "Extract files", - "extracting": "Extracting files…" - }, - "settings": { - "downloads_path": "Downloads path", - "change": "Update", - "notifications": "Notifications", - "enable_download_notifications": "When a download is complete", - "enable_repack_list_notifications": "When a new repack is added", - "real_debrid_api_token_label": "Real-Debrid API token", - "quit_app_instead_hiding": "Don't hide Hydra when closing", - "launch_with_system": "Launch Hydra on system start-up", - "general": "General", - "behavior": "Behavior", - "download_sources": "Download sources", - "language": "Language", - "api_token": "API Token", - "enable_real_debrid": "Enable Real-Debrid", - "real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to quickly download files, only limited by your internet speed.", - "debrid_invalid_token": "Invalid API token", - "debrid_api_token_hint": "You can get your API token <0>here", - "real_debrid_free_account_error": "The account \"{{username}}\" is a free account. Please subscribe to Real-Debrid", - "debrid_linked_message": "Account \"{{username}}\" linked", - "save_changes": "Save changes", - "changes_saved": "Changes successfully saved", - "download_sources_description": "Hydra will fetch the download links from these sources. The source URL must be a direct link to a .json file containing the download links.", - "validate_download_source": "Validate", - "remove_download_source": "Remove", - "add_download_source": "Add source", - "download_count_zero": "No download options", - "download_count_one": "{{countFormatted}} download option", - "download_count_other": "{{countFormatted}} download options", - "download_source_url": "Download source URL", - "add_download_source_description": "Insert the URL of the .json file", - "download_source_up_to_date": "Up-to-date", - "download_source_errored": "Errored", - "sync_download_sources": "Sync sources", - "removed_download_source": "Download source removed", - "removed_download_sources": "Download sources removed", - "cancel_button_confirmation_delete_all_sources": "No", - "confirm_button_confirmation_delete_all_sources": "Yes, delete everything", - "title_confirmation_delete_all_sources": "Delete all download sources", - "description_confirmation_delete_all_sources": "You will delete all download sources", - "button_delete_all_sources": "Remove all", - "added_download_source": "Added download source", - "download_sources_synced": "All download sources are synced", - "insert_valid_json_url": "Insert a valid JSON url", - "found_download_option_zero": "No download option found", - "found_download_option_one": "Found {{countFormatted}} download option", - "found_download_option_other": "Found {{countFormatted}} download options", - "import": "Import", - "public": "Public", - "private": "Private", - "friends_only": "Friends only", - "privacy": "Privacy", - "profile_visibility": "Profile visibility", - "profile_visibility_description": "Choose who can see your profile and library", - "required_field": "This field is required", - "source_already_exists": "This source has already been added", - "must_be_valid_url": "The source must be a valid URL", - "blocked_users": "Blocked users", - "user_unblocked": "User has been unblocked", - "enable_achievement_notifications": "When an achievement is unlocked", - "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", - "account": "Account", - "no_users_blocked": "You have no blocked users", - "subscription_active_until": "Your Hydra Cloud is active until {{date}}", - "manage_subscription": "Manage subscription", - "update_email": "Update email", - "update_password": "Update password", - "current_email": "Current email:", - "no_email_account": "You have not set an email yet", - "account_data_updated_successfully": "Account data updated successfully", - "renew_subscription": "Renew Hydra Cloud", - "subscription_expired_at": "Your subscription expired at {{date}}", - "no_subscription": "Enjoy Hydra in the best possible way", - "become_subscriber": "Be Hydra Cloud", - "subscription_renew_cancelled": "Automatic renewal is disabled", - "subscription_renews_on": "Your subscription renews on {{date}}", - "bill_sent_until": "Your next bill will be sent until this day", - "no_themes": "Seems like you don't have any themes yet, but no worries, click here to create your first masterpiece.", - "editor_tab_code": "Code", - "editor_tab_info": "Info", - "editor_tab_save": "Save", - "web_store": "Web store", - "clear_themes": "Clear", - "create_theme": "Create", - "create_theme_modal_title": "Create custom theme", - "create_theme_modal_description": "Create a new theme to customize Hydra's appearance", - "theme_name": "Name", - "insert_theme_name": "Insert theme name", - "set_theme": "Set theme", - "unset_theme": "Unset theme", - "delete_theme": "Delete theme", - "edit_theme": "Edit theme", - "delete_all_themes": "Delete all themes", - "delete_all_themes_description": "This will delete all your custom themes", - "delete_theme_description": "This will delete the theme {{theme}}", - "cancel": "Cancel", - "appearance": "Appearance", - "enable_torbox": "Enable TorBox", - "torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.", - "torbox_account_linked": "TorBox account linked", - "create_real_debrid_account": "Click here if you don't have a Real-Debrid account yet", - "create_torbox_account": "Click here if you don't have a TorBox account yet", - "real_debrid_account_linked": "Real-Debrid account linked", - "name_min_length": "Theme name must be at least 3 characters long", - "import_theme": "Import theme", - "import_theme_description": "You will import {{theme}} from the theme store", - "error_importing_theme": "Error importing theme", - "theme_imported": "Theme imported successfully", - "enable_friend_request_notifications": "When a friend request is received", - "enable_auto_install": "Download updates automatically", - "common_redist": "Common redistributables", - "common_redist_description": "Common redistributables are required to run some games. Installing them is recommended to avoid issues.", - "install_common_redist": "Install", - "installing_common_redist": "Installing…", - "show_download_speed_in_megabytes": "Show download speed in megabytes per second", - "extract_files_by_default": "Extract files by default after download", - "enable_steam_achievements": "Enable search for Steam achievements", - "achievement_custom_notification_position": "Achievement custom notification position", - "top-left": "Top left", - "top-center": "Top center", - "top-right": "Top right", - "bottom-left": "Bottom left", - "bottom-center": "Bottom center", - "bottom-right": "Bottom right", - "enable_achievement_custom_notifications": "Enable achievement custom notifications", - "alignment": "Alignment", - "variation": "Variation", - "default": "Default", - "rare": "Rare", - "platinum": "Platinum", - "hidden": "Hidden", - "test_notification": "Test notification", - "notification_preview": "Achievement Notification Preview", - "enable_friend_start_game_notifications": "When a friend starts playing a game" - }, - "notifications": { - "download_complete": "Download complete", - "game_ready_to_install": "{{title}} is ready to install", - "repack_list_updated": "Repack list updated", - "repack_count_one": "{{count}} repack added", - "repack_count_other": "{{count}} repacks added", - "new_update_available": "Version {{version}} available", - "restart_to_install_update": "Restart Hydra to install the update", - "notification_achievement_unlocked_title": "Achievement unlocked for {{game}}", - "notification_achievement_unlocked_body": "{{achievement}} and other {{count}} were unlocked", - "new_friend_request_description": "{{displayName}} sent you a friend request", - "new_friend_request_title": "New friend request", - "extraction_complete": "Extraction complete", - "game_extracted": "{{title}} extracted successfully", - "friend_started_playing_game": "{{displayName}} started playing a game", - "test_achievement_notification_title": "This is a test notification", - "test_achievement_notification_description": "Pretty cool, huh?" - }, - "system_tray": { - "open": "Open Hydra", - "quit": "Quit" - }, - "game_card": { - "available_one": "Available", - "available_other": "Available", - "no_downloads": "No downloads available" - }, - "binary_not_found_modal": { - "title": "Programs not installed", - "description": "Wine or Lutris executables were not found on your system", - "instructions": "Check the correct way to install any of them on your Linux distro so that the game can run normally" - }, - "modal": { - "close": "Close button" - }, - "forms": { - "toggle_password_visibility": "Toggle password visibility" - }, - "user_profile": { - "amount_hours": "{{amount}} hours", - "amount_minutes": "{{amount}} minutes", - "last_time_played": "Last played {{period}}", - "activity": "Recent Activity", - "library": "Library", - "total_play_time": "Total playtime", - "no_recent_activity_title": "Hmmm… nothing here", - "no_recent_activity_description": "You haven't played any games recently. It's time to change that!", - "display_name": "Display name", - "saving": "Saving", - "save": "Save", - "edit_profile": "Edit Profile", - "saved_successfully": "Saved successfully", - "try_again": "Please, try again", - "sign_out_modal_title": "Are you sure?", - "cancel": "Cancel", - "successfully_signed_out": "Successfully signed out", - "sign_out": "Sign out", - "playing_for": "Playing for {{amount}}", - "sign_out_modal_text": "Your library is linked with your current account. When signing out, your library will not be visible anymore, and any progress will not be saved. Continue with sign out?", - "add_friends": "Add Friends", - "add": "Add", - "friend_code": "Friend code", - "see_profile": "See profile", - "sending": "Sending", - "friend_request_sent": "Friend request sent", - "friends": "Friends", - "friends_list": "Friends list", - "user_not_found": "User not found", - "block_user": "Block user", - "add_friend": "Add friend", - "request_sent": "Request sent", - "request_received": "Request received", - "accept_request": "Accept request", - "ignore_request": "Ignore request", - "cancel_request": "Cancel request", - "undo_friendship": "Undo friendship", - "request_accepted": "Request accepted", - "user_blocked_successfully": "User blocked successfully", - "user_block_modal_text": "This will block {{displayName}}", - "blocked_users": "Blocked users", - "unblock": "Unblock", - "no_friends_added": "You have no added friends", - "pending": "Pending", - "no_pending_invites": "You have no pending invites", - "no_blocked_users": "You have no blocked users", - "friend_code_copied": "Friend code copied", - "undo_friendship_modal_text": "This will undo your friendship with {{displayName}}", - "privacy_hint": "To adjust who can see this, go to the <0>Settings", - "locked_profile": "This profile is private", - "image_process_failure": "Failure while processing the image", - "required_field": "This field is required", - "displayname_min_length": "Display name must be at least 3 characters long", - "displayname_max_length": "Display name must be at most 50 characters long", - "report_profile": "Report this profile", - "report_reason": "Why are you reporting this profile?", - "report_description": "Additional information", - "report_description_placeholder": "Additional information", - "report": "Report", - "report_reason_hate": "Hate speech", - "report_reason_sexual_content": "Sexual content", - "report_reason_violence": "Violence", - "report_reason_spam": "Spam", - "report_reason_other": "Other", - "profile_reported": "Profile reported", - "your_friend_code": "Your friend code:", - "upload_banner": "Upload banner", - "uploading_banner": "Uploading banner…", - "background_image_updated": "Background image updated", - "stats": "Stats", - "achievements": "achievements", - "games": "Games", - "top_percentile": "Top {{percentile}}%", - "ranking_updated_weekly": "Ranking is updated weekly", - "playing": "Playing {{game}}", - "achievements_unlocked": "Achievements Unlocked", - "earned_points": "Earned points", - "show_achievements_on_profile": "Show your achievements on your profile", - "show_points_on_profile": "Show your earned points on your profile", - "error_adding_friend": "Could not send friend request. Please check friend code", - "friend_code_length_error": "Friend code must have 8 characters" - }, - "achievement": { - "achievement_unlocked": "Achievement unlocked", - "user_achievements": "{{displayName}}'s Achievements", - "your_achievements": "Your Achievements", - "unlocked_at": "Unlocked at: {{date}}", - "subscription_needed": "A Hydra Cloud subscription is required to see this content", - "new_achievements_unlocked": "Unlocked {{achievementCount}} new achievements from {{gameCount}} games", - "achievement_progress": "{{unlockedCount}}/{{totalCount}} achievements", - "achievements_unlocked_for_game": "Unlocked {{achievementCount}} new achievements for {{gameTitle}}", - "hidden_achievement_tooltip": "This is a hidden achievement", - "achievement_earn_points": "Earn {{points}} points with this achievement", - "earned_points": "Earned points:", - "available_points": "Available points:", - "how_to_earn_achievements_points": "How to earn achievements points?" - }, - "hydra_cloud": { - "subscription_tour_title": "Hydra Cloud Subscription", - "subscribe_now": "Subscribe now", - "cloud_saving": "Cloud saving", - "cloud_achievements": "Save your achievements on the cloud", - "animated_profile_picture": "Animated profile pictures", - "premium_support": "Premium Support", - "show_and_compare_achievements": "Show and compare your achievements to other users", - "animated_profile_banner": "Animated profile banner", - "hydra_cloud": "Hydra Cloud", - "hydra_cloud_feature_found": "You've just discovered a Hydra Cloud feature!", - "learn_more": "Learn More", - "debrid_description": "Download up to 4x faster with Nimbus" - } -} +{ + "language_name": "English", + "app": { + "successfully_signed_in": "Successfully signed in" + }, + "home": { + "featured": "Featured", + "surprise_me": "Surprise me", + "no_results": "No results found", + "start_typing": "Starting typing to search...", + "hot": "Hot now", + "weekly": "📅 Top games of the week", + "achievements": "🏆 Games to beat" + }, + "sidebar": { + "catalogue": "Catalogue", + "downloads": "Downloads", + "settings": "Settings", + "my_library": "My library", + "downloading_metadata": "{{title}} (Downloading metadata…)", + "paused": "{{title}} (Paused)", + "downloading": "{{title}} ({{percentage}} - Downloading…)", + "filter": "Filter library", + "home": "Home", + "queued": "{{title}} (Queued)", + "game_has_no_executable": "Game has no executable selected", + "sign_in": "Sign in", + "friends": "Friends", + "need_help": "Need help?", + "favorites": "Favorites", + "playable_button_title": "Show only games you can play now" + }, + "header": { + "search": "Search games", + "home": "Home", + "catalogue": "Catalogue", + "downloads": "Downloads", + "search_results": "Search results", + "settings": "Settings", + "version_available_install": "Version {{version}} available. Click here to restart and install.", + "version_available_download": "Version {{version}} available. Click here to download." + }, + "bottom_panel": { + "no_downloads_in_progress": "No downloads in progress", + "downloading_metadata": "Downloading {{title}} metadata…", + "downloading": "Downloading {{title}}… ({{percentage}} complete) - Completion {{eta}} - {{speed}}", + "calculating_eta": "Downloading {{title}}… ({{percentage}} complete) - Calculating remaining time…", + "checking_files": "Checking {{title}} files… ({{percentage}} complete)", + "installing_common_redist": "{{log}}…", + "installation_complete": "Installation complete", + "installation_complete_message": "Common redistributables installed successfully" + }, + "catalogue": { + "search": "Filter…", + "developers": "Developers", + "genres": "Genres", + "tags": "Tags", + "publishers": "Publishers", + "download_sources": "Download sources", + "result_count": "{{resultCount}} results", + "filter_count": "{{filterCount}} available", + "clear_filters": "Clear {{filterCount}} selected" + }, + "game_details": { + "open_download_options": "Open download options", + "download_options_zero": "No download option", + "download_options_one": "{{count}} download option", + "download_options_other": "{{count}} download options", + "updated_at": "Updated {{updated_at}}", + "install": "Install", + "resume": "Resume", + "pause": "Pause", + "cancel": "Cancel", + "remove": "Remove", + "space_left_on_disk": "{{space}} left on disk", + "eta": "Conclusion {{eta}}", + "calculating_eta": "Calculating remaining time…", + "downloading_metadata": "Downloading metadata…", + "filter": "Filter repacks", + "requirements": "System requirements", + "minimum": "Minimum", + "recommended": "Recommended", + "paused": "Paused", + "release_date": "Released on {{date}}", + "publisher": "Published by {{publisher}}", + "hours": "hours", + "minutes": "minutes", + "amount_hours": "{{amount}} hours", + "amount_minutes": "{{amount}} minutes", + "accuracy": "{{accuracy}}% accuracy", + "add_to_library": "Add to library", + "already_in_library": "Already in library", + "remove_from_library": "Remove from library", + "no_downloads": "No downloads available", + "play_time": "Played for {{amount}}", + "last_time_played": "Last played {{period}}", + "not_played_yet": "You haven't played {{title}} yet", + "next_suggestion": "Next suggestion", + "play": "Play", + "deleting": "Deleting installer…", + "close": "Close", + "playing_now": "Playing now", + "change": "Change", + "repacks_modal_description": "Choose the repack you want to download", + "select_folder_hint": "To change the default folder, go to the <0>Settings", + "download_now": "Download now", + "no_shop_details": "Could not retrieve shop details.", + "download_options": "Download options", + "download_path": "Download path", + "previous_screenshot": "Previous screenshot", + "next_screenshot": "Next screenshot", + "screenshot": "Screenshot {{number}}", + "open_screenshot": "Open screenshot {{number}}", + "download_settings": "Download settings", + "downloader": "Downloader", + "select_executable": "Select", + "no_executable_selected": "No executable selected", + "open_folder": "Open folder", + "open_download_location": "See downloaded files", + "create_shortcut": "Create desktop shortcut", + "clear": "Clear", + "remove_files": "Remove files", + "remove_from_library_title": "Are you sure?", + "remove_from_library_description": "This will remove {{game}} from your library", + "options": "Options", + "executable_section_title": "Executable", + "executable_section_description": "Path of the file that will be executed when \"Play\" is clicked", + "downloads_section_title": "Downloads", + "downloads_section_description": "Check out updates or other versions of this game", + "danger_zone_section_title": "Danger zone", + "danger_zone_section_description": "Remove this game from your library or the files downloaded by Hydra", + "download_in_progress": "Download in progress", + "download_paused": "Download paused", + "last_downloaded_option": "Last downloaded option", + "create_steam_shortcut": "Create Steam shortcut", + "create_shortcut_success": "Shortcut created successfully", + "you_might_need_to_restart_steam": "You might need to restart Steam to see the changes", + "create_shortcut_error": "Error creating shortcut", + "nsfw_content_title": "This game contains inappropriate content", + "nsfw_content_description": "{{title}} contains content that may not be suitable for all ages. Are you sure you want to continue?", + "allow_nsfw_content": "Continue", + "refuse_nsfw_content": "Go back", + "stats": "Stats", + "download_count": "Downloads", + "player_count": "Active players", + "download_error": "This download option is not available", + "download": "Download", + "executable_path_in_use": "Executable already in use by \"{{game}}\"", + "warning": "Warning:", + "hydra_needs_to_remain_open": "for this download, Hydra needs to remain open util it's completed. If Hydra closes before completing, you will lose your progress.", + "achievements": "Achievements", + "achievements_count": "Achievements {{unlockedCount}}/{{achievementsCount}}", + "cloud_save": "Cloud save", + "cloud_save_description": "Save your progress in the cloud and continue playing on any device", + "backups": "Backups", + "install_backup": "Install", + "delete_backup": "Delete", + "create_backup": "New backup", + "last_backup_date": "Last backup on {{date}}", + "no_backup_preview": "No save games were found for this title", + "restoring_backup": "Restoring backup ({{progress}} complete)…", + "uploading_backup": "Uploading backup…", + "no_backups": "You haven't created any backups for this game yet", + "backup_uploaded": "Backup uploaded", + "backup_deleted": "Backup deleted", + "backup_restored": "Backup restored", + "see_all_achievements": "See all achievements", + "sign_in_to_see_achievements": "Sign in to see achievements", + "mapping_method_automatic": "Automatic", + "mapping_method_manual": "Manual", + "mapping_method_label": "Mapping method", + "files_automatically_mapped": "Files automatically mapped", + "no_backups_created": "No backups created for this game", + "manage_files": "Manage files", + "loading_save_preview": "Searching for save games…", + "wine_prefix": "Wine Prefix", + "wine_prefix_description": "The Wine prefix used to run this game", + "launch_options": "Launch Options", + "launch_options_description": "Advanced users may choose to enter modifications to their launch options (experimental feature)", + "launch_options_placeholder": "No parameter specified", + "no_download_option_info": "No information available", + "backup_deletion_failed": "Failed to delete backup", + "max_number_of_artifacts_reached": "Maximum number of backups reached for this game", + "achievements_not_sync": "See how to synchronize your achievements", + "manage_files_description": "Manage which files will be backed up and restored", + "select_folder": "Select folder", + "backup_from": "Backup from {{date}}", + "automatic_backup_from": "Automatic backup from {{date}}", + "enable_automatic_cloud_sync": "Enable automatic cloud sync", + "custom_backup_location_set": "Custom backup location set", + "no_directory_selected": "No directory selected", + "no_write_permission": "Cannot download into this directory. Click here to learn more.", + "reset_achievements": "Reset achievements", + "reset_achievements_description": "This will reset all achievements for {{game}}", + "reset_achievements_title": "Are you sure?", + "reset_achievements_success": "Achievements successfully reset", + "reset_achievements_error": "Failed to reset achievements", + "download_error_gofile_quota_exceeded": "You have exceeded your Gofile monthly quota. Please await the quota to reset.", + "download_error_real_debrid_account_not_authorized": "Your Real-Debrid account is not authorized to make new downloads. Please check your account settings and try again.", + "download_error_not_cached_on_real_debrid": "This download is not available on Real-Debrid and polling download status from Real-Debrid is not yet available.", + "download_error_not_cached_on_torbox": "This download is not available on TorBox and polling download status from TorBox is not yet available.", + "download_error_not_cached_on_hydra": "This download is not available on Nimbus.", + "game_removed_from_favorites": "Game removed from favorites", + "game_added_to_favorites": "Game added to favorites", + "automatically_extract_downloaded_files": "Automatically extract downloaded files", + "create_start_menu_shortcut": "Create Start Menu shortcut", + "invalid_wine_prefix_path": "Invalid Wine prefix path", + "invalid_wine_prefix_path_description": "The path to the Wine prefix is invalid. Please check the path and try again.", + "missing_wine_prefix": "Wine prefix is required to create a backup on Linux", + "artifact_renamed": "Backup renamed successfully", + "rename_artifact": "Rename Backup", + "rename_artifact_description": "Rename the backup to a more descriptive name", + "artifact_name_label": "Backup name", + "artifact_name_placeholder": "Enter a name for the backup", + "save_changes": "Save changes", + "required_field": "This field is required", + "max_length_field": "This field must be less than {{length}} characters", + "freeze_backup": "Pin it so it's not overwritten by automatic backups", + "unfreeze_backup": "Unpin it", + "backup_frozen": "Backup pinned", + "backup_unfrozen": "Backup unpinned", + "backup_freeze_failed": "Failed to freeze backup", + "backup_freeze_failed_description": "You must leave at least one free slot for automatic backups", + "game_details": "Game Details", + "currency_symbol": "$", + "currency_country": "us", + "prices": "Prices", + "no_prices_found": "No prices found", + "retail_price": "Retail price", + "keyshop_price": "Keyshop price", + "historical_retail": "Historical retail", + "historical_keyshop": "Historical keyshop", + "supported_languages": "Supported languages", + "language": "Language", + "caption": "Caption", + "audio": "Audio" + }, + "activation": { + "title": "Activate Hydra", + "installation_id": "Installation ID:", + "enter_activation_code": "Enter your activation code", + "message": "If you don't know where to ask for this, then you shouldn't have this.", + "activate": "Activate", + "loading": "Loading…" + }, + "downloads": { + "resume": "Resume", + "pause": "Pause", + "eta": "Conclusion {{eta}}", + "paused": "Paused", + "verifying": "Verifying…", + "completed": "Completed", + "removed": "Not downloaded", + "cancel": "Cancel", + "filter": "Filter downloaded games", + "remove": "Remove", + "downloading_metadata": "Downloading metadata…", + "deleting": "Deleting installer…", + "delete": "Remove installer", + "delete_modal_title": "Are you sure?", + "delete_modal_description": "This will remove all the installation files from your computer", + "install": "Install", + "download_in_progress": "In progress", + "queued_downloads": "Queued downloads", + "downloads_completed": "Completed", + "queued": "Queued", + "no_downloads_title": "Such empty", + "no_downloads_description": "You haven't downloaded anything with Hydra yet, but it's never too late to start.", + "checking_files": "Checking files…", + "seeding": "Seeding", + "stop_seeding": "Stop seeding", + "resume_seeding": "Resume seeding", + "options": "Manage", + "extract": "Extract files", + "extracting": "Extracting files…" + }, + "settings": { + "downloads_path": "Downloads path", + "change": "Update", + "notifications": "Notifications", + "enable_download_notifications": "When a download is complete", + "gg_deals_api_key_description": "gg deals api key. Used to show the lowest price. (https://gg.deals/api/)", + "enable_repack_list_notifications": "When a new repack is added", + "real_debrid_api_token_label": "Real-Debrid API token", + "quit_app_instead_hiding": "Don't hide Hydra when closing", + "launch_with_system": "Launch Hydra on system start-up", + "general": "General", + "behavior": "Behavior", + "download_sources": "Download sources", + "language": "Language", + "api_token": "API Token", + "enable_real_debrid": "Enable Real-Debrid", + "real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to quickly download files, only limited by your internet speed.", + "debrid_invalid_token": "Invalid API token", + "debrid_api_token_hint": "You can get your API token <0>here", + "real_debrid_free_account_error": "The account \"{{username}}\" is a free account. Please subscribe to Real-Debrid", + "debrid_linked_message": "Account \"{{username}}\" linked", + "save_changes": "Save changes", + "changes_saved": "Changes successfully saved", + "download_sources_description": "Hydra will fetch the download links from these sources. The source URL must be a direct link to a .json file containing the download links.", + "validate_download_source": "Validate", + "remove_download_source": "Remove", + "add_download_source": "Add source", + "download_count_zero": "No download options", + "download_count_one": "{{countFormatted}} download option", + "download_count_other": "{{countFormatted}} download options", + "download_source_url": "Download source URL", + "add_download_source_description": "Insert the URL of the .json file", + "download_source_up_to_date": "Up-to-date", + "download_source_errored": "Errored", + "sync_download_sources": "Sync sources", + "removed_download_source": "Download source removed", + "removed_download_sources": "Download sources removed", + "cancel_button_confirmation_delete_all_sources": "No", + "confirm_button_confirmation_delete_all_sources": "Yes, delete everything", + "title_confirmation_delete_all_sources": "Delete all download sources", + "description_confirmation_delete_all_sources": "You will delete all download sources", + "button_delete_all_sources": "Remove all", + "added_download_source": "Added download source", + "download_sources_synced": "All download sources are synced", + "insert_valid_json_url": "Insert a valid JSON url", + "found_download_option_zero": "No download option found", + "found_download_option_one": "Found {{countFormatted}} download option", + "found_download_option_other": "Found {{countFormatted}} download options", + "import": "Import", + "public": "Public", + "private": "Private", + "friends_only": "Friends only", + "privacy": "Privacy", + "profile_visibility": "Profile visibility", + "profile_visibility_description": "Choose who can see your profile and library", + "required_field": "This field is required", + "source_already_exists": "This source has already been added", + "must_be_valid_url": "The source must be a valid URL", + "blocked_users": "Blocked users", + "user_unblocked": "User has been unblocked", + "enable_achievement_notifications": "When an achievement is unlocked", + "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", + "account": "Account", + "no_users_blocked": "You have no blocked users", + "subscription_active_until": "Your Hydra Cloud is active until {{date}}", + "manage_subscription": "Manage subscription", + "update_email": "Update email", + "update_password": "Update password", + "current_email": "Current email:", + "no_email_account": "You have not set an email yet", + "account_data_updated_successfully": "Account data updated successfully", + "renew_subscription": "Renew Hydra Cloud", + "subscription_expired_at": "Your subscription expired at {{date}}", + "no_subscription": "Enjoy Hydra in the best possible way", + "become_subscriber": "Be Hydra Cloud", + "subscription_renew_cancelled": "Automatic renewal is disabled", + "subscription_renews_on": "Your subscription renews on {{date}}", + "bill_sent_until": "Your next bill will be sent until this day", + "no_themes": "Seems like you don't have any themes yet, but no worries, click here to create your first masterpiece.", + "editor_tab_code": "Code", + "editor_tab_info": "Info", + "editor_tab_save": "Save", + "web_store": "Web store", + "clear_themes": "Clear", + "create_theme": "Create", + "create_theme_modal_title": "Create custom theme", + "create_theme_modal_description": "Create a new theme to customize Hydra's appearance", + "theme_name": "Name", + "insert_theme_name": "Insert theme name", + "set_theme": "Set theme", + "unset_theme": "Unset theme", + "delete_theme": "Delete theme", + "edit_theme": "Edit theme", + "delete_all_themes": "Delete all themes", + "delete_all_themes_description": "This will delete all your custom themes", + "delete_theme_description": "This will delete the theme {{theme}}", + "cancel": "Cancel", + "appearance": "Appearance", + "enable_torbox": "Enable TorBox", + "torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.", + "torbox_account_linked": "TorBox account linked", + "create_real_debrid_account": "Click here if you don't have a Real-Debrid account yet", + "create_torbox_account": "Click here if you don't have a TorBox account yet", + "real_debrid_account_linked": "Real-Debrid account linked", + "name_min_length": "Theme name must be at least 3 characters long", + "import_theme": "Import theme", + "import_theme_description": "You will import {{theme}} from the theme store", + "error_importing_theme": "Error importing theme", + "theme_imported": "Theme imported successfully", + "enable_friend_request_notifications": "When a friend request is received", + "enable_auto_install": "Download updates automatically", + "common_redist": "Common redistributables", + "common_redist_description": "Common redistributables are required to run some games. Installing them is recommended to avoid issues.", + "install_common_redist": "Install", + "installing_common_redist": "Installing…", + "show_download_speed_in_megabytes": "Show download speed in megabytes per second", + "extract_files_by_default": "Extract files by default after download", + "enable_steam_achievements": "Enable search for Steam achievements", + "achievement_custom_notification_position": "Achievement custom notification position", + "top-left": "Top left", + "top-center": "Top center", + "top-right": "Top right", + "bottom-left": "Bottom left", + "bottom-center": "Bottom center", + "bottom-right": "Bottom right", + "enable_achievement_custom_notifications": "Enable achievement custom notifications", + "alignment": "Alignment", + "variation": "Variation", + "default": "Default", + "rare": "Rare", + "platinum": "Platinum", + "hidden": "Hidden", + "test_notification": "Test notification", + "notification_preview": "Achievement Notification Preview", + "enable_friend_start_game_notifications": "When a friend starts playing a game" + }, + "notifications": { + "download_complete": "Download complete", + "game_ready_to_install": "{{title}} is ready to install", + "repack_list_updated": "Repack list updated", + "repack_count_one": "{{count}} repack added", + "repack_count_other": "{{count}} repacks added", + "new_update_available": "Version {{version}} available", + "restart_to_install_update": "Restart Hydra to install the update", + "notification_achievement_unlocked_title": "Achievement unlocked for {{game}}", + "notification_achievement_unlocked_body": "{{achievement}} and other {{count}} were unlocked", + "new_friend_request_description": "{{displayName}} sent you a friend request", + "new_friend_request_title": "New friend request", + "extraction_complete": "Extraction complete", + "game_extracted": "{{title}} extracted successfully", + "friend_started_playing_game": "{{displayName}} started playing a game", + "test_achievement_notification_title": "This is a test notification", + "test_achievement_notification_description": "Pretty cool, huh?" + }, + "system_tray": { + "open": "Open Hydra", + "quit": "Quit" + }, + "game_card": { + "available_one": "Available", + "available_other": "Available", + "no_downloads": "No downloads available" + }, + "binary_not_found_modal": { + "title": "Programs not installed", + "description": "Wine or Lutris executables were not found on your system", + "instructions": "Check the correct way to install any of them on your Linux distro so that the game can run normally" + }, + "modal": { + "close": "Close button" + }, + "forms": { + "toggle_password_visibility": "Toggle password visibility" + }, + "user_profile": { + "amount_hours": "{{amount}} hours", + "amount_minutes": "{{amount}} minutes", + "last_time_played": "Last played {{period}}", + "activity": "Recent Activity", + "library": "Library", + "total_play_time": "Total playtime", + "no_recent_activity_title": "Hmmm… nothing here", + "no_recent_activity_description": "You haven't played any games recently. It's time to change that!", + "display_name": "Display name", + "saving": "Saving", + "save": "Save", + "edit_profile": "Edit Profile", + "saved_successfully": "Saved successfully", + "try_again": "Please, try again", + "sign_out_modal_title": "Are you sure?", + "cancel": "Cancel", + "successfully_signed_out": "Successfully signed out", + "sign_out": "Sign out", + "playing_for": "Playing for {{amount}}", + "sign_out_modal_text": "Your library is linked with your current account. When signing out, your library will not be visible anymore, and any progress will not be saved. Continue with sign out?", + "add_friends": "Add Friends", + "add": "Add", + "friend_code": "Friend code", + "see_profile": "See profile", + "sending": "Sending", + "friend_request_sent": "Friend request sent", + "friends": "Friends", + "friends_list": "Friends list", + "user_not_found": "User not found", + "block_user": "Block user", + "add_friend": "Add friend", + "request_sent": "Request sent", + "request_received": "Request received", + "accept_request": "Accept request", + "ignore_request": "Ignore request", + "cancel_request": "Cancel request", + "undo_friendship": "Undo friendship", + "request_accepted": "Request accepted", + "user_blocked_successfully": "User blocked successfully", + "user_block_modal_text": "This will block {{displayName}}", + "blocked_users": "Blocked users", + "unblock": "Unblock", + "no_friends_added": "You have no added friends", + "pending": "Pending", + "no_pending_invites": "You have no pending invites", + "no_blocked_users": "You have no blocked users", + "friend_code_copied": "Friend code copied", + "undo_friendship_modal_text": "This will undo your friendship with {{displayName}}", + "privacy_hint": "To adjust who can see this, go to the <0>Settings", + "locked_profile": "This profile is private", + "image_process_failure": "Failure while processing the image", + "required_field": "This field is required", + "displayname_min_length": "Display name must be at least 3 characters long", + "displayname_max_length": "Display name must be at most 50 characters long", + "report_profile": "Report this profile", + "report_reason": "Why are you reporting this profile?", + "report_description": "Additional information", + "report_description_placeholder": "Additional information", + "report": "Report", + "report_reason_hate": "Hate speech", + "report_reason_sexual_content": "Sexual content", + "report_reason_violence": "Violence", + "report_reason_spam": "Spam", + "report_reason_other": "Other", + "profile_reported": "Profile reported", + "your_friend_code": "Your friend code:", + "upload_banner": "Upload banner", + "uploading_banner": "Uploading banner…", + "background_image_updated": "Background image updated", + "stats": "Stats", + "achievements": "achievements", + "games": "Games", + "top_percentile": "Top {{percentile}}%", + "ranking_updated_weekly": "Ranking is updated weekly", + "playing": "Playing {{game}}", + "achievements_unlocked": "Achievements Unlocked", + "earned_points": "Earned points", + "show_achievements_on_profile": "Show your achievements on your profile", + "show_points_on_profile": "Show your earned points on your profile", + "error_adding_friend": "Could not send friend request. Please check friend code", + "friend_code_length_error": "Friend code must have 8 characters" + }, + "achievement": { + "achievement_unlocked": "Achievement unlocked", + "user_achievements": "{{displayName}}'s Achievements", + "your_achievements": "Your Achievements", + "unlocked_at": "Unlocked at: {{date}}", + "subscription_needed": "A Hydra Cloud subscription is required to see this content", + "new_achievements_unlocked": "Unlocked {{achievementCount}} new achievements from {{gameCount}} games", + "achievement_progress": "{{unlockedCount}}/{{totalCount}} achievements", + "achievements_unlocked_for_game": "Unlocked {{achievementCount}} new achievements for {{gameTitle}}", + "hidden_achievement_tooltip": "This is a hidden achievement", + "achievement_earn_points": "Earn {{points}} points with this achievement", + "earned_points": "Earned points:", + "available_points": "Available points:", + "how_to_earn_achievements_points": "How to earn achievements points?" + }, + "hydra_cloud": { + "subscription_tour_title": "Hydra Cloud Subscription", + "subscribe_now": "Subscribe now", + "cloud_saving": "Cloud saving", + "cloud_achievements": "Save your achievements on the cloud", + "animated_profile_picture": "Animated profile pictures", + "premium_support": "Premium Support", + "show_and_compare_achievements": "Show and compare your achievements to other users", + "animated_profile_banner": "Animated profile banner", + "hydra_cloud": "Hydra Cloud", + "hydra_cloud_feature_found": "You've just discovered a Hydra Cloud feature!", + "learn_more": "Learn More", + "debrid_description": "Download up to 4x faster with Nimbus" + } +} diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json old mode 100644 new mode 100755 index 22f5b533..a4bcf826 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -1,545 +1,559 @@ -{ - "language_name": "Português (Brasil)", - "app": { - "successfully_signed_in": "Autenticado com sucesso" - }, - "home": { - "featured": "Destaques", - "hot": "Populares", - "weekly": "📅 Mais baixados da semana", - "achievements": "🏆 Pra platinar", - "surprise_me": "Surpreenda-me", - "no_results": "Nenhum resultado encontrado", - "start_typing": "Comece a digitar para pesquisar…" - }, - "sidebar": { - "catalogue": "Catálogo", - "downloads": "Downloads", - "settings": "Ajustes", - "my_library": "Biblioteca", - "downloading_metadata": "{{title}} (Baixando metadados…)", - "paused": "{{title}} (Pausado)", - "downloading": "{{title}} ({{percentage}} - Baixando…)", - "filter": "Buscar", - "home": "Início", - "queued": "{{title}} (Na fila)", - "game_has_no_executable": "Jogo não possui executável selecionado", - "sign_in": "Login", - "friends": "Amigos", - "need_help": "Precisa de ajuda?", - "favorites": "Favoritos" - }, - "header": { - "search": "Buscar jogos", - "catalogue": "Catálogo", - "downloads": "Downloads", - "search_results": "Resultados da busca", - "settings": "Ajustes", - "home": "Início", - "version_available_install": "Versão {{version}} disponível. Clique aqui para reiniciar e instalar.", - "version_available_download": "Versão {{version}} disponível. Clique aqui para fazer o download." - }, - "bottom_panel": { - "no_downloads_in_progress": "Sem downloads em andamento", - "downloading_metadata": "Baixando metadados de {{title}}…", - "downloading": "Baixando {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}", - "calculating_eta": "Baixando {{title}}… ({{percentage}} concluído) - Calculando tempo restante…", - "checking_files": "Verificando arquivos de {{title}}…", - "installing_common_redist": "{{log}}…", - "installation_complete": "Instalação concluída", - "installation_complete_message": "Componentes recomendados instalados com sucesso" - }, - "game_details": { - "open_download_options": "Ver opções de download", - "download_options_zero": "Sem opções de download", - "download_options_one": "{{count}} opção de download", - "download_options_other": "{{count}} opções de download", - "updated_at": "Atualizado {{updated_at}}", - "resume": "Retomar", - "pause": "Pausar", - "cancel": "Cancelar", - "remove": "Remover", - "space_left_on_disk": "{{space}} livres em disco", - "eta": "Conclusão {{eta}}", - "calculating_eta": "Calculando tempo restante…", - "downloading_metadata": "Baixando metadados…", - "filter": "Filtrar repacks", - "requirements": "Requisitos de sistema", - "minimum": "Mínimos", - "recommended": "Recomendados", - "paused": "Pausado", - "release_date": "Lançado em {{date}}", - "publisher": "Publicado por {{publisher}}", - "hours": "horas", - "minutes": "minutos", - "amount_hours": "{{amount}} horas", - "amount_minutes": "{{amount}} minutos", - "accuracy": "{{accuracy}}% de precisão", - "add_to_library": "Adicionar à biblioteca", - "already_in_library": "Já está na biblioteca", - "remove_from_library": "Remover da biblioteca", - "no_downloads": "Nenhum download disponível", - "play_time": "Jogou por {{amount}}", - "next_suggestion": "Próxima sugestão", - "install": "Instalar", - "last_time_played": "Última sessão {{period}}", - "play": "Jogar", - "not_played_yet": "Você ainda não jogou {{title}}", - "close": "Fechar", - "deleting": "Excluindo instalador…", - "playing_now": "Jogando agora", - "change": "Explorar", - "repacks_modal_description": "Escolha o repack do jogo que deseja baixar", - "select_folder_hint": "Para trocar o diretório padrão, acesse a <0>Tela de Ajustes", - "download_now": "Iniciar download", - "no_shop_details": "Não foi possível obter os detalhes da loja.", - "download_options": "Opções de download", - "download_path": "Diretório de download", - "previous_screenshot": "Captura de tela anterior", - "next_screenshot": "Próxima captura de tela", - "screenshot": "Captura de tela {{number}}", - "open_screenshot": "Ver captura de tela {{number}}", - "download_settings": "Ajustes do download", - "downloader": "Downloader", - "select_executable": "Explorar", - "no_executable_selected": "Nenhum executável selecionado", - "open_folder": "Abrir pasta", - "open_download_location": "Ver arquivos baixados", - "create_shortcut": "Criar atalho na área de trabalho", - "remove_files": "Remover arquivos", - "options": "Gerenciar", - "remove_from_library_description": "Isso irá remover {{game}} da sua biblioteca", - "remove_from_library_title": "Tem certeza?", - "executable_section_title": "Executável", - "executable_section_description": "O caminho do arquivo que será executado ao clicar em \"Jogar\"", - "downloads_section_title": "Downloads", - "downloads_section_description": "Confira atualizações ou versões diferentes para este mesmo título", - "danger_zone_section_title": "Zona de perigo", - "danger_zone_section_description": "Remova o jogo da sua biblioteca ou os arquivos que foram baixados pelo Hydra", - "download_in_progress": "Download em andamento", - "download_paused": "Download pausado", - "last_downloaded_option": "Última opção baixada", - "create_steam_shortcut": "Criar atalho na Steam", - "create_shortcut_success": "Atalho criado com sucesso", - "you_might_need_to_restart_steam": "Você pode precisar reiniciar a Steam para ver as alterações", - "create_shortcut_error": "Erro ao criar atalho", - "nsfw_content_title": "Este jogo contém conteúdo inapropriado", - "nsfw_content_description": "{{title}} contém conteúdo que pode não ser apropriado para todas as idades. Você deseja continuar?", - "allow_nsfw_content": "Continuar", - "refuse_nsfw_content": "Voltar", - "stats": "Estatísticas", - "download_count": "Downloads", - "player_count": "Jogadores ativos", - "download_error": "Essa opção de download falhou", - "download": "Baixar", - "executable_path_in_use": "Executável em uso por \"{{game}}\"", - "warning": "Aviso:", - "hydra_needs_to_remain_open": "para este download, o Hydra precisa ficar aberto até a conclusão. Caso o Hydra encerre antes da conclusão, perderá seu progresso.", - "achievements": "Conquistas", - "achievements_count": "Conquistas ({{unlockedCount}}/{{achievementsCount}})", - "cloud_save": "Salvamento em nuvem", - "cloud_save_description": "Mantenha seu progresso na nuvem e continue de onde parou em qualquer dispositivo", - "backups": "Backups", - "install_backup": "Restaurar", - "delete_backup": "Apagar", - "create_backup": "Novo backup", - "last_backup_date": "Último backup em {{date}}", - "no_backup_preview": "Não foi possível encontrar nenhum salvamento para este jogo", - "restoring_backup": "Restaurando backup ({{progress}} concluído)…", - "uploading_backup": "Criando backup…", - "no_backups": "Você ainda não fez nenhum backup deste jogo", - "backup_uploaded": "Backup criado", - "backup_deleted": "Backup apagado", - "backup_restored": "Backup restaurado", - "see_all_achievements": "Ver todas as conquistas", - "sign_in_to_see_achievements": "Faça login para ver as conquistas", - "mapping_method_automatic": "Automático", - "mapping_method_manual": "Manual", - "mapping_method_label": "Método de mapeamento", - "files_automatically_mapped": "Arquivos automaticamente mapeados", - "no_backups_created": "Nenhum backup criado para este jogo", - "manage_files": "Gerenciar arquivos", - "loading_save_preview": "Buscando por arquivos de salvamento…", - "wine_prefix": "Prefixo Wine", - "wine_prefix_description": "O prefixo Wine que foi utilizado para instalar o jogo", - "launch_options": "Opções de Inicialização", - "launch_options_description": "Usuários avançados podem adicionar opções de inicialização no jogo (experimental)", - "launch_options_placeholder": "Nenhum parâmetro informado", - "no_download_option_info": "Sem informações disponíveis", - "backup_deletion_failed": "Falha ao apagar backup", - "max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo", - "achievements_not_sync": "Veja como exibir suas conquistas no perfil", - "backup_from": "Backup de {{date}}", - "automatic_backup_from": "Backup automático de {{date}}", - "enable_automatic_cloud_sync": "Habilitar sincronização automática na nuvem", - "custom_backup_location_set": "Localização customizada selecionada", - "select_folder": "Selecione a pasta", - "manage_files_description": "Gerencie quais arquivos serão feitos backup", - "clear": "Limpar", - "no_directory_selected": "Nenhum diretório selecionado", - "reset_achievements": "Resetar conquistas", - "reset_achievements_description": "Isso irá resetar todas as conquistas de {{game}}", - "reset_achievements_title": "Tem certeza?", - "reset_achievements_success": "Conquistas resetadas com sucesso", - "reset_achievements_error": "Falha ao resetar conquistas", - "no_write_permission": "Não é possível baixar nesse diretório. Clique aqui para saber mais.", - "download_error_gofile_quota_exceeded": "Você excedeu sua cota mensal do Gofile. Por favor, aguarde a cota resetar.", - "download_error_real_debrid_account_not_authorized": "Sua conta do Real-Debrid não está autorizada a fazer novos downloads. Por favor, verifique sua assinatura e tente novamente.", - "download_error_not_cached_on_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.", - "download_error_not_cached_on_torbox": "Este download não está disponível no TorBox e a verificação do status do download não está disponível.", - "download_error_not_cached_on_hydra": "Este download não está disponível no Nimbus.", - "game_removed_from_favorites": "Jogo removido dos favoritos", - "game_added_to_favorites": "Jogo adicionado aos favoritos", - "automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados", - "create_start_menu_shortcut": "Criar atalho no Menu Iniciar", - "invalid_wine_prefix_path": "Caminho do prefixo Wine inválido", - "invalid_wine_prefix_path_description": "O caminho para o prefixo Wine é inválido. Por favor, verifique o caminho e tente novamente.", - "artifact_renamed": "Backup renomeado com sucesso", - "rename_artifact": "Renomear Backup", - "rename_artifact_description": "Renomeie o backup para um nome mais descritivo", - "artifact_name_label": "Nome do backup", - "artifact_name_placeholder": "Insira um nome para o backup", - "save_changes": "Salvar mudanças", - "required_field": "Este campo é obrigatório", - "max_length_field": "Este campo deve ter menos de {{length}} caracteres", - "freeze_backup": "Fixar para não ser apagado por backups automáticos", - "unfreeze_backup": "Remover dos fixados", - "backup_frozen": "Backup fixado", - "backup_unfrozen": "Backup removido dos fixados", - "backup_freeze_failed": "Falha ao fixar backup", - "backup_freeze_failed_description": "Você deve deixar pelo menos um espaço livre para backups automáticos" - }, - "activation": { - "title": "Ativação", - "installation_id": "ID da instalação:", - "enter_activation_code": "Insira seu código de ativação", - "message": "Se você não sabe onde conseguir o código, talvez você não devesse estar aqui.", - "activate": "Ativar", - "loading": "Carregando…" - }, - "downloads": { - "resume": "Retomar", - "pause": "Pausar", - "eta": "Conclusão {{eta}}", - "paused": "Pausado", - "verifying": "Verificando…", - "completed": "Concluído", - "removed": "Cancelado", - "cancel": "Cancelar", - "filter": "Filtrar jogos baixados", - "remove": "Remover", - "downloading_metadata": "Baixando metadados…", - "delete": "Remover instalador", - "delete_modal_description": "Isso removerá todos os arquivos de instalação do seu computador", - "delete_modal_title": "Tem certeza?", - "deleting": "Excluindo instalador…", - "install": "Instalar", - "download_in_progress": "Baixando agora", - "queued_downloads": "Na fila", - "downloads_completed": "Concluído", - "queued": "Na fila", - "no_downloads_title": "Nada por aqui…", - "no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.", - "checking_files": "Verificando arquivos…", - "seeding": "Semeando", - "stop_seeding": "Parar de semear", - "resume_seeding": "Semear", - "options": "Gerenciar", - "extract": "Extrair arquivos", - "extracting": "Extraindo arquivos…" - }, - "settings": { - "downloads_path": "Diretório dos downloads", - "change": "Explorar...", - "notifications": "Notificações", - "enable_download_notifications": "Quando um download for concluído", - "enable_repack_list_notifications": "Quando a lista de repacks for atualizada", - "real_debrid_api_token_label": "Token de API do Real-Debrid", - "quit_app_instead_hiding": "Encerrar o Hydra em vez de apenas minimizá-lo ao fechar", - "launch_with_system": "Iniciar o Hydra junto com o sistema", - "general": "Geral", - "behavior": "Comportamento", - "download_sources": "Fontes de download", - "language": "Idioma", - "api_token": "Token de API", - "enable_real_debrid": "Habilitar Real-Debrid", - "debrid_api_token_hint": "Você pode obter seu token de API <0>aqui", - "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos instantaneamente e com a melhor velocidade da sua Internet.", - "debrid_invalid_token": "Token de API inválido", - "real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, assine a Real-Debrid", - "debrid_linked_message": "Conta \"{{username}}\" vinculada", - "save_changes": "Salvar mudanças", - "changes_saved": "Ajustes salvos com sucesso", - "download_sources_description": "Hydra vai buscar links de download em todas as fontes habilitadas. A URL da fonte deve ser um link direto para um arquivo .json contendo uma lista de links.", - "validate_download_source": "Validar", - "remove_download_source": "Remover", - "add_download_source": "Adicionar fonte", - "download_count_zero": "Sem downloads na lista", - "download_count_one": "{{countFormatted}} download na lista", - "download_count_other": "{{countFormatted}} downloads na lista", - "download_source_url": "URL da fonte", - "add_download_source_description": "Insira a URL contendo o arquivo .json", - "download_source_up_to_date": "Sincronizada", - "download_source_errored": "Falhou", - "sync_download_sources": "Sincronizar", - "removed_download_source": "Fonte removida", - "removed_download_sources": "Fontes removidas", - "cancel_button_confirmation_delete_all_sources": "Não", - "confirm_button_confirmation_delete_all_sources": "Sim, excluir tudo", - "title_confirmation_delete_all_sources": "Remover todas as fontes de download", - "description_confirmation_delete_all_sources": "Você irá remover todas as fontes de download. Deseja prosseguir?", - "button_delete_all_sources": "Remover todas", - "added_download_source": "Fonte adicionada", - "download_sources_synced": "As fontes foram sincronizadas", - "insert_valid_json_url": "Insira a url de um JSON válido", - "found_download_option_zero": "Nenhuma opção de download encontrada", - "found_download_option_one": "{{countFormatted}} opção de download encontrada", - "found_download_option_other": "{{countFormatted}} opções de download encontradas", - "import": "Importar", - "privacy": "Privacidade", - "private": "Privado", - "friends_only": "Apenas amigos", - "public": "Público", - "profile_visibility": "Visibilidade do perfil", - "profile_visibility_description": "Escolha quem pode ver seu perfil e biblioteca", - "required_field": "Este campo é obrigatório", - "source_already_exists": "Essa fonte já foi adicionada", - "must_be_valid_url": "A fonte deve ser uma URL válida", - "blocked_users": "Usuários bloqueados", - "user_unblocked": "Usuário desbloqueado", - "enable_achievement_notifications": "Quando uma conquista é desbloqueada", - "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", - "account": "Conta", - "no_users_blocked": "Você não bloqueou nenhum usuário", - "subscription_active_until": "Sua assinatura Hydra Cloud ficará ativa até {{date}}", - "manage_subscription": "Gerenciar assinatura", - "update_email": "Atualizar email", - "update_password": "Atualizar senha", - "current_email": "Email atual:", - "no_email_account": "Você ainda não adicionou um email a sua conta", - "account_data_updated_successfully": "Dados da conta atualizados com sucesso", - "renew_subscription": "Renovar Hydra Cloud", - "subscription_expired_at": "Sua assinatura expirou em {{date}}", - "no_subscription": "Aproveite o Hydra da melhor forma possível", - "become_subscriber": "Seja Hydra Cloud", - "subscription_renew_cancelled": "A renovação automática está desativada", - "subscription_renews_on": "Sua assinatura renova dia {{date}}", - "bill_sent_until": "Sua próxima cobrança será enviada até esse dia", - "no_themes": "Parece que você ainda não tem nenhum tema. Não se preocupe, clique aqui para criar sua primeira obra de arte.", - "editor_tab_save": "Salvar", - "web_store": "Loja de temas", - "clear_themes": "Limpar", - "create_theme": "Criar", - "create_theme_modal_title": "Criar tema customizado", - "create_theme_modal_description": "Criar novo tema para customizar a aparência do Hydra", - "theme_name": "Nome", - "insert_theme_name": "Insira o nome do tema", - "set_theme": "Habilitar tema", - "unset_theme": "Desabilitar tema", - "delete_theme": "Deletar tema", - "edit_theme": "Editar tema", - "delete_all_themes": "Deletar todos os temas", - "delete_all_themes_description": "Isso irá deletar todos os seus temas", - "delete_theme_description": "Isso irá deletar o tema {{theme}}", - "cancel": "Cancelar", - "appearance": "Aparência", - "enable_torbox": "Habilitar TorBox", - "torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.", - "torbox_account_linked": "Conta do TorBox vinculada", - "create_real_debrid_account": "Clique aqui se você ainda não tem uma conta do Real-Debrid", - "create_torbox_account": "Clique aqui se você ainda não tem uma conta do TorBox", - "real_debrid_account_linked": "Conta Real-Debrid associada", - "name_min_length": "O nome do tema deve ter pelo menos 3 caracteres", - "import_theme": "Importar tema", - "import_theme_description": "Você irá importar {{theme}} da loja de temas", - "error_importing_theme": "Erro ao importar tema", - "theme_imported": "Tema importado com sucesso", - "enable_friend_request_notifications": "Quando um pedido de amizade é recebido", - "enable_auto_install": "Baixar atualizações automaticamente", - "common_redist": "Componentes recomendados", - "common_redist_description": "Componentes recomendados são necessários para executar alguns jogos. A instalação deles é recomendada para evitar problemas.", - "install_common_redist": "Instalar", - "installing_common_redist": "Instalando…", - "show_download_speed_in_megabytes": "Exibir taxas de download em megabytes por segundo", - "extract_files_by_default": "Extrair arquivos automaticamente após o download", - "enable_steam_achievements": "Habilitar busca por conquistas da Steam", - "enable_achievement_custom_notifications": "Habilitar notificações customizadas de conquistas", - "top-left": "Superior esquerdo", - "top-center": "Superior central", - "top-right": "Superior direito", - "bottom-left": "Inferior esquerdo", - "bottom-right": "Inferior direito", - "bottom-center": "Inferior central", - "achievement_custom_notification_position": "Posição das notificações customizadas de conquista", - "alignment": "Alinhamento", - "variation": "Variação", - "default": "Padrão", - "rare": "Rara", - "platinum": "Platina", - "hidden": "Oculta", - "test_notification": "Testar notificação", - "notification_preview": "Prévia da Notificação de Conquistas", - "enable_friend_start_game_notifications": "Quando um amigo iniciar um jogo" - }, - "notifications": { - "download_complete": "Download concluído", - "game_ready_to_install": "{{title}} está pronto para ser instalado", - "repack_list_updated": "Lista de repacks atualizada", - "repack_count_one": "{{count}} novo repack", - "repack_count_other": "{{count}} novos repacks", - "new_update_available": "Versão {{version}} disponível", - "restart_to_install_update": "Reinicie o Hydra para instalar a nova versão", - "new_friend_request_title": "Novo pedido de amizade", - "new_friend_request_description": "{{displayName}} te enviou um pedido de amizade", - "extraction_complete": "Extração concluída", - "game_extracted": "{{title}} extraído com sucesso", - "friend_started_playing_game": "{{displayName}} começou a jogar", - "test_achievement_notification_title": "Esta é uma notificação de teste", - "test_achievement_notification_description": "Bem legal, né?" - }, - "system_tray": { - "open": "Abrir Hydra", - "quit": "Fechar" - }, - "game_card": { - "available_one": "Disponível", - "available_other": "Disponíveis", - "no_downloads": "Sem downloads disponíveis" - }, - "binary_not_found_modal": { - "title": "Programas não instalados", - "description": "Os executáveis do Wine ou Lutris não foram encontrados em seu sistema.", - "instructions": "Verifique a forma correta de instalar algum deles no seu distro Linux, garantindo assim a execução normal do jogo" - }, - "catalogue": { - "search": "Filtrar…", - "developers": "Desenvolvedores", - "genres": "Gêneros", - "tags": "Marcadores", - "publishers": "Distribuidoras", - "download_sources": "Fontes de download", - "result_count": "{{resultCount}} resultados", - "filter_count": "{{filterCount}} disponíveis", - "clear_filters": "Limpar {{filterCount}} selecionados" - }, - "modal": { - "close": "Botão de fechar" - }, - "forms": { - "toggle_password_visibility": "Alternar visibilidade da senha" - }, - "user_profile": { - "amount_hours": "{{amount}} horas", - "amount_minutes": "{{amount}} minutos", - "last_time_played": "Última sessão {{period}}", - "activity": "Atividades recentes", - "library": "Biblioteca", - "total_play_time": "Tempo total de jogo", - "no_recent_activity_title": "Hmmm… nada por aqui", - "no_recent_activity_description": "Parece que você não jogou nada recentemente. Que tal começar agora?", - "display_name": "Nome de exibição", - "saving": "Salvando…", - "save": "Salvar", - "edit_profile": "Editar perfil", - "saved_successfully": "Salvo com sucesso", - "try_again": "Por favor, tente novamente", - "cancel": "Cancelar", - "successfully_signed_out": "Deslogado com sucesso", - "sign_out": "Sair da conta", - "sign_out_modal_title": "Deseja mesmo sair?", - "playing_for": "Jogando por {{amount}}", - "sign_out_modal_text": "Sua biblioteca de jogos está associada com a sua conta atual. Ao sair, sua biblioteca não aparecerá mais no Hydra e qualquer progresso não será salvo. Deseja continuar?", - "add_friends": "Adicionar Amigos", - "friend_code": "Código de amigo", - "see_profile": "Ver perfil", - "friend_request_sent": "Pedido de amizade enviado", - "friends": "Amigos", - "add": "Adicionar", - "sending": "Enviando", - "friends_list": "Lista de amigos", - "user_not_found": "Usuário não encontrado", - "block_user": "Bloquear", - "add_friend": "Adicionar amigo", - "request_sent": "Pedido enviado", - "request_received": "Pedido recebido", - "accept_request": "Aceitar pedido", - "ignore_request": "Ignorar pedido", - "cancel_request": "Cancelar pedido", - "undo_friendship": "Desfazer amizade", - "request_accepted": "Pedido de amizade aceito", - "user_blocked_successfully": "Usuário bloqueado com sucesso", - "user_block_modal_text": "Bloquear {{displayName}}", - "blocked_users": "Usuários bloqueados", - "unblock": "Desbloquear", - "no_friends_added": "Você ainda não possui amigos adicionados", - "pending": "Pendentes", - "no_pending_invites": "Você não possui convites de amizade pendentes", - "no_blocked_users": "Você não tem nenhum usuário bloqueado", - "friend_code_copied": "Código de amigo copiado", - "undo_friendship_modal_text": "Isso irá remover sua amizade com {{displayName}}", - "privacy_hint": "Pra controlar quem pode ver seu perfil, acesse a <0>Tela de Configurações", - "image_process_failure": "Falha ao processar a imagem", - "required_field": "Este campo é obrigatório", - "displayname_min_length": "Nome de exibição deve ter pelo menos 3 caracteres", - "displayname_max_length": "Nome de exibição deve ter no máximo 50 caracteres", - "locked_profile": "Este perfil é privado", - "report_profile": "Reportar este perfil", - "report_reason": "Por que você deseja reportar este perfil?", - "report_description": "Informações adicionais", - "report_description_placeholder": "Insira aqui", - "report": "Reportar", - "report_reason_hate": "Discurso de ódio", - "report_reason_sexual_content": "Conteúdo sexual", - "report_reason_violence": "Violência", - "report_reason_spam": "Spam", - "report_reason_other": "Outro", - "profile_reported": "Perfil reportado", - "your_friend_code": "Seu código de amigo:", - "upload_banner": "Carregar banner", - "uploading_banner": "Carregando banner…", - "background_image_updated": "Imagem de fundo salva", - "stats": "Estatísticas", - "achievements": "conquistas", - "games": "Jogos", - "ranking_updated_weekly": "O ranking é atualizado semanalmente", - "playing": "Jogando {{game}}", - "achievements_unlocked": "Conquistas desbloqueadas", - "earned_points": "Pontos ganhos", - "show_achievements_on_profile": "Exiba suas conquistas no perfil", - "show_points_on_profile": "Exiba seus pontos ganhos no perfil", - "error_adding_friend": "Não foi possível enviar o pedido de amizade. Verifique o código de amizade inserido", - "friend_code_length_error": "Código de amigo deve ter 8 caracteres" - }, - "achievement": { - "achievement_unlocked": "Conquista desbloqueada", - "your_achievements": "Suas Conquistas", - "user_achievements": "Conquistas de {{displayName}}", - "unlocked_at": "Desbloqueada em: {{date}}", - "subscription_needed": "Você precisa de uma assinatura Hydra Cloud para visualizar este conteúdo", - "new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos", - "achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas", - "achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}", - "hidden_achievement_tooltip": "Esta é uma conquista oculta", - "achievement_earn_points": "Ganhe {{points}} pontos com essa conquista", - "earned_points": "Pontos ganhos:", - "available_points": "Pontos disponíveis:", - "how_to_earn_achievements_points": "Como desbloquear pontos nas conquistas?" - }, - "hydra_cloud": { - "subscription_tour_title": "Assinatura Hydra Cloud", - "hydra_cloud": "Hydra Cloud", - "subscribe_now": "Inscreva-se agora", - "cloud_achievements": "Salvamento de conquistas em nuvem", - "animated_profile_picture": "Fotos de perfil animadas", - "premium_support": "Suporte Premium", - "show_and_compare_achievements": "Exiba e compare suas conquistas com outros usuários", - "animated_profile_banner": "Banner animado no perfil", - "cloud_saving": "Saves de jogos em nuvem", - "hydra_cloud_feature_found": "Você descobriu uma funcionalidade Hydra Cloud!", - "learn_more": "Saiba mais", - "debrid_description": "Baixe até 4x mais rápido com Nimbus" - } -} +{ + "language_name": "Português (Brasil)", + "app": { + "successfully_signed_in": "Autenticado com sucesso" + }, + "home": { + "featured": "Destaques", + "hot": "Populares", + "weekly": "📅 Mais baixados da semana", + "achievements": "🏆 Pra platinar", + "surprise_me": "Surpreenda-me", + "no_results": "Nenhum resultado encontrado", + "start_typing": "Comece a digitar para pesquisar…" + }, + "sidebar": { + "catalogue": "Catálogo", + "downloads": "Downloads", + "settings": "Ajustes", + "my_library": "Biblioteca", + "downloading_metadata": "{{title}} (Baixando metadados…)", + "paused": "{{title}} (Pausado)", + "downloading": "{{title}} ({{percentage}} - Baixando…)", + "filter": "Buscar", + "home": "Início", + "queued": "{{title}} (Na fila)", + "game_has_no_executable": "Jogo não possui executável selecionado", + "sign_in": "Login", + "friends": "Amigos", + "need_help": "Precisa de ajuda?", + "favorites": "Favoritos" + }, + "header": { + "search": "Buscar jogos", + "catalogue": "Catálogo", + "downloads": "Downloads", + "search_results": "Resultados da busca", + "settings": "Ajustes", + "home": "Início", + "version_available_install": "Versão {{version}} disponível. Clique aqui para reiniciar e instalar.", + "version_available_download": "Versão {{version}} disponível. Clique aqui para fazer o download." + }, + "bottom_panel": { + "no_downloads_in_progress": "Sem downloads em andamento", + "downloading_metadata": "Baixando metadados de {{title}}…", + "downloading": "Baixando {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}", + "calculating_eta": "Baixando {{title}}… ({{percentage}} concluído) - Calculando tempo restante…", + "checking_files": "Verificando arquivos de {{title}}…", + "installing_common_redist": "{{log}}…", + "installation_complete": "Instalação concluída", + "installation_complete_message": "Componentes recomendados instalados com sucesso" + }, + "game_details": { + "open_download_options": "Ver opções de download", + "download_options_zero": "Sem opções de download", + "download_options_one": "{{count}} opção de download", + "download_options_other": "{{count}} opções de download", + "updated_at": "Atualizado {{updated_at}}", + "resume": "Retomar", + "pause": "Pausar", + "cancel": "Cancelar", + "remove": "Remover", + "space_left_on_disk": "{{space}} livres em disco", + "eta": "Conclusão {{eta}}", + "calculating_eta": "Calculando tempo restante…", + "downloading_metadata": "Baixando metadados…", + "filter": "Filtrar repacks", + "requirements": "Requisitos de sistema", + "minimum": "Mínimos", + "recommended": "Recomendados", + "paused": "Pausado", + "release_date": "Lançado em {{date}}", + "publisher": "Publicado por {{publisher}}", + "hours": "horas", + "minutes": "minutos", + "amount_hours": "{{amount}} horas", + "amount_minutes": "{{amount}} minutos", + "accuracy": "{{accuracy}}% de precisão", + "add_to_library": "Adicionar à biblioteca", + "already_in_library": "Já está na biblioteca", + "remove_from_library": "Remover da biblioteca", + "no_downloads": "Nenhum download disponível", + "play_time": "Jogou por {{amount}}", + "next_suggestion": "Próxima sugestão", + "install": "Instalar", + "last_time_played": "Última sessão {{period}}", + "play": "Jogar", + "not_played_yet": "Você ainda não jogou {{title}}", + "close": "Fechar", + "deleting": "Excluindo instalador…", + "playing_now": "Jogando agora", + "change": "Explorar", + "repacks_modal_description": "Escolha o repack do jogo que deseja baixar", + "select_folder_hint": "Para trocar o diretório padrão, acesse a <0>Tela de Ajustes", + "download_now": "Iniciar download", + "no_shop_details": "Não foi possível obter os detalhes da loja.", + "download_options": "Opções de download", + "download_path": "Diretório de download", + "previous_screenshot": "Captura de tela anterior", + "next_screenshot": "Próxima captura de tela", + "screenshot": "Captura de tela {{number}}", + "open_screenshot": "Ver captura de tela {{number}}", + "download_settings": "Ajustes do download", + "downloader": "Downloader", + "select_executable": "Explorar", + "no_executable_selected": "Nenhum executável selecionado", + "open_folder": "Abrir pasta", + "open_download_location": "Ver arquivos baixados", + "create_shortcut": "Criar atalho na área de trabalho", + "remove_files": "Remover arquivos", + "options": "Gerenciar", + "remove_from_library_description": "Isso irá remover {{game}} da sua biblioteca", + "remove_from_library_title": "Tem certeza?", + "executable_section_title": "Executável", + "executable_section_description": "O caminho do arquivo que será executado ao clicar em \"Jogar\"", + "downloads_section_title": "Downloads", + "downloads_section_description": "Confira atualizações ou versões diferentes para este mesmo título", + "danger_zone_section_title": "Zona de perigo", + "danger_zone_section_description": "Remova o jogo da sua biblioteca ou os arquivos que foram baixados pelo Hydra", + "download_in_progress": "Download em andamento", + "download_paused": "Download pausado", + "last_downloaded_option": "Última opção baixada", + "create_steam_shortcut": "Criar atalho na Steam", + "create_shortcut_success": "Atalho criado com sucesso", + "you_might_need_to_restart_steam": "Você pode precisar reiniciar a Steam para ver as alterações", + "create_shortcut_error": "Erro ao criar atalho", + "nsfw_content_title": "Este jogo contém conteúdo inapropriado", + "nsfw_content_description": "{{title}} contém conteúdo que pode não ser apropriado para todas as idades. Você deseja continuar?", + "allow_nsfw_content": "Continuar", + "refuse_nsfw_content": "Voltar", + "stats": "Estatísticas", + "download_count": "Downloads", + "player_count": "Jogadores ativos", + "download_error": "Essa opção de download falhou", + "download": "Baixar", + "executable_path_in_use": "Executável em uso por \"{{game}}\"", + "warning": "Aviso:", + "hydra_needs_to_remain_open": "para este download, o Hydra precisa ficar aberto até a conclusão. Caso o Hydra encerre antes da conclusão, perderá seu progresso.", + "achievements": "Conquistas", + "achievements_count": "Conquistas ({{unlockedCount}}/{{achievementsCount}})", + "cloud_save": "Salvamento em nuvem", + "cloud_save_description": "Mantenha seu progresso na nuvem e continue de onde parou em qualquer dispositivo", + "backups": "Backups", + "install_backup": "Restaurar", + "delete_backup": "Apagar", + "create_backup": "Novo backup", + "last_backup_date": "Último backup em {{date}}", + "no_backup_preview": "Não foi possível encontrar nenhum salvamento para este jogo", + "restoring_backup": "Restaurando backup ({{progress}} concluído)…", + "uploading_backup": "Criando backup…", + "no_backups": "Você ainda não fez nenhum backup deste jogo", + "backup_uploaded": "Backup criado", + "backup_deleted": "Backup apagado", + "backup_restored": "Backup restaurado", + "see_all_achievements": "Ver todas as conquistas", + "sign_in_to_see_achievements": "Faça login para ver as conquistas", + "mapping_method_automatic": "Automático", + "mapping_method_manual": "Manual", + "mapping_method_label": "Método de mapeamento", + "files_automatically_mapped": "Arquivos automaticamente mapeados", + "no_backups_created": "Nenhum backup criado para este jogo", + "manage_files": "Gerenciar arquivos", + "loading_save_preview": "Buscando por arquivos de salvamento…", + "wine_prefix": "Prefixo Wine", + "wine_prefix_description": "O prefixo Wine que foi utilizado para instalar o jogo", + "launch_options": "Opções de Inicialização", + "launch_options_description": "Usuários avançados podem adicionar opções de inicialização no jogo (experimental)", + "launch_options_placeholder": "Nenhum parâmetro informado", + "no_download_option_info": "Sem informações disponíveis", + "backup_deletion_failed": "Falha ao apagar backup", + "max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo", + "achievements_not_sync": "Veja como exibir suas conquistas no perfil", + "backup_from": "Backup de {{date}}", + "automatic_backup_from": "Backup automático de {{date}}", + "enable_automatic_cloud_sync": "Habilitar sincronização automática na nuvem", + "custom_backup_location_set": "Localização customizada selecionada", + "select_folder": "Selecione a pasta", + "manage_files_description": "Gerencie quais arquivos serão feitos backup", + "clear": "Limpar", + "no_directory_selected": "Nenhum diretório selecionado", + "reset_achievements": "Resetar conquistas", + "reset_achievements_description": "Isso irá resetar todas as conquistas de {{game}}", + "reset_achievements_title": "Tem certeza?", + "reset_achievements_success": "Conquistas resetadas com sucesso", + "reset_achievements_error": "Falha ao resetar conquistas", + "no_write_permission": "Não é possível baixar nesse diretório. Clique aqui para saber mais.", + "download_error_gofile_quota_exceeded": "Você excedeu sua cota mensal do Gofile. Por favor, aguarde a cota resetar.", + "download_error_real_debrid_account_not_authorized": "Sua conta do Real-Debrid não está autorizada a fazer novos downloads. Por favor, verifique sua assinatura e tente novamente.", + "download_error_not_cached_on_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.", + "download_error_not_cached_on_torbox": "Este download não está disponível no TorBox e a verificação do status do download não está disponível.", + "download_error_not_cached_on_hydra": "Este download não está disponível no Nimbus.", + "game_removed_from_favorites": "Jogo removido dos favoritos", + "game_added_to_favorites": "Jogo adicionado aos favoritos", + "automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados", + "create_start_menu_shortcut": "Criar atalho no Menu Iniciar", + "invalid_wine_prefix_path": "Caminho do prefixo Wine inválido", + "invalid_wine_prefix_path_description": "O caminho para o prefixo Wine é inválido. Por favor, verifique o caminho e tente novamente.", + "artifact_renamed": "Backup renomeado com sucesso", + "rename_artifact": "Renomear Backup", + "rename_artifact_description": "Renomeie o backup para um nome mais descritivo", + "artifact_name_label": "Nome do backup", + "artifact_name_placeholder": "Insira um nome para o backup", + "save_changes": "Salvar mudanças", + "required_field": "Este campo é obrigatório", + "max_length_field": "Este campo deve ter menos de {{length}} caracteres", + "freeze_backup": "Fixar para não ser apagado por backups automáticos", + "unfreeze_backup": "Remover dos fixados", + "backup_frozen": "Backup fixado", + "backup_unfrozen": "Backup removido dos fixados", + "backup_freeze_failed": "Falha ao fixar backup", + "backup_freeze_failed_description": "Você deve deixar pelo menos um espaço livre para backups automáticos", + "game_details": "Detalhes do Jogo", + "currency_symbol": "R$", + "currency_country": "br", + "prices": "Preços", + "no_prices_found": "Nenhum preço encontrado", + "retail_price": "Preço de lojas oficiais", + "keyshop_price": "Preço em keyshops", + "historical_retail": "Preço histórico de lojas oficiais", + "historical_keyshop": "Preço histórico em keyshops", + "supported_languages": "Idiomas suportados", + "language": "Idioma", + "caption": "Legenda", + "audio": "Áudio" + }, + "activation": { + "title": "Ativação", + "installation_id": "ID da instalação:", + "enter_activation_code": "Insira seu código de ativação", + "message": "Se você não sabe onde conseguir o código, talvez você não devesse estar aqui.", + "activate": "Ativar", + "loading": "Carregando…" + }, + "downloads": { + "resume": "Retomar", + "pause": "Pausar", + "eta": "Conclusão {{eta}}", + "paused": "Pausado", + "verifying": "Verificando…", + "completed": "Concluído", + "removed": "Cancelado", + "cancel": "Cancelar", + "filter": "Filtrar jogos baixados", + "remove": "Remover", + "downloading_metadata": "Baixando metadados…", + "delete": "Remover instalador", + "delete_modal_description": "Isso removerá todos os arquivos de instalação do seu computador", + "delete_modal_title": "Tem certeza?", + "deleting": "Excluindo instalador…", + "install": "Instalar", + "download_in_progress": "Baixando agora", + "queued_downloads": "Na fila", + "downloads_completed": "Concluído", + "queued": "Na fila", + "no_downloads_title": "Nada por aqui…", + "no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.", + "checking_files": "Verificando arquivos…", + "seeding": "Semeando", + "stop_seeding": "Parar de semear", + "resume_seeding": "Semear", + "options": "Gerenciar", + "extract": "Extrair arquivos", + "extracting": "Extraindo arquivos…" + }, + "settings": { + "downloads_path": "Diretório dos downloads", + "change": "Explorar...", + "notifications": "Notificações", + "enable_download_notifications": "Quando um download for concluído", + "gg_deals_api_key_description": "gg deals api key. Usado para mostrar o menor preço. (https://gg.deals/api/)", + "enable_repack_list_notifications": "Quando a lista de repacks for atualizada", + "real_debrid_api_token_label": "Token de API do Real-Debrid", + "quit_app_instead_hiding": "Encerrar o Hydra em vez de apenas minimizá-lo ao fechar", + "launch_with_system": "Iniciar o Hydra junto com o sistema", + "general": "Geral", + "behavior": "Comportamento", + "download_sources": "Fontes de download", + "language": "Idioma", + "api_token": "Token de API", + "enable_real_debrid": "Habilitar Real-Debrid", + "debrid_api_token_hint": "Você pode obter seu token de API <0>aqui", + "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos instantaneamente e com a melhor velocidade da sua Internet.", + "debrid_invalid_token": "Token de API inválido", + "real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, assine a Real-Debrid", + "debrid_linked_message": "Conta \"{{username}}\" vinculada", + "save_changes": "Salvar mudanças", + "changes_saved": "Ajustes salvos com sucesso", + "download_sources_description": "Hydra vai buscar links de download em todas as fontes habilitadas. A URL da fonte deve ser um link direto para um arquivo .json contendo uma lista de links.", + "validate_download_source": "Validar", + "remove_download_source": "Remover", + "add_download_source": "Adicionar fonte", + "download_count_zero": "Sem downloads na lista", + "download_count_one": "{{countFormatted}} download na lista", + "download_count_other": "{{countFormatted}} downloads na lista", + "download_source_url": "URL da fonte", + "add_download_source_description": "Insira a URL contendo o arquivo .json", + "download_source_up_to_date": "Sincronizada", + "download_source_errored": "Falhou", + "sync_download_sources": "Sincronizar", + "removed_download_source": "Fonte removida", + "removed_download_sources": "Fontes removidas", + "cancel_button_confirmation_delete_all_sources": "Não", + "confirm_button_confirmation_delete_all_sources": "Sim, excluir tudo", + "title_confirmation_delete_all_sources": "Remover todas as fontes de download", + "description_confirmation_delete_all_sources": "Você irá remover todas as fontes de download. Deseja prosseguir?", + "button_delete_all_sources": "Remover todas", + "added_download_source": "Fonte adicionada", + "download_sources_synced": "As fontes foram sincronizadas", + "insert_valid_json_url": "Insira a url de um JSON válido", + "found_download_option_zero": "Nenhuma opção de download encontrada", + "found_download_option_one": "{{countFormatted}} opção de download encontrada", + "found_download_option_other": "{{countFormatted}} opções de download encontradas", + "import": "Importar", + "privacy": "Privacidade", + "private": "Privado", + "friends_only": "Apenas amigos", + "public": "Público", + "profile_visibility": "Visibilidade do perfil", + "profile_visibility_description": "Escolha quem pode ver seu perfil e biblioteca", + "required_field": "Este campo é obrigatório", + "source_already_exists": "Essa fonte já foi adicionada", + "must_be_valid_url": "A fonte deve ser uma URL válida", + "blocked_users": "Usuários bloqueados", + "user_unblocked": "Usuário desbloqueado", + "enable_achievement_notifications": "Quando uma conquista é desbloqueada", + "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", + "account": "Conta", + "no_users_blocked": "Você não bloqueou nenhum usuário", + "subscription_active_until": "Sua assinatura Hydra Cloud ficará ativa até {{date}}", + "manage_subscription": "Gerenciar assinatura", + "update_email": "Atualizar email", + "update_password": "Atualizar senha", + "current_email": "Email atual:", + "no_email_account": "Você ainda não adicionou um email a sua conta", + "account_data_updated_successfully": "Dados da conta atualizados com sucesso", + "renew_subscription": "Renovar Hydra Cloud", + "subscription_expired_at": "Sua assinatura expirou em {{date}}", + "no_subscription": "Aproveite o Hydra da melhor forma possível", + "become_subscriber": "Seja Hydra Cloud", + "subscription_renew_cancelled": "A renovação automática está desativada", + "subscription_renews_on": "Sua assinatura renova dia {{date}}", + "bill_sent_until": "Sua próxima cobrança será enviada até esse dia", + "no_themes": "Parece que você ainda não tem nenhum tema. Não se preocupe, clique aqui para criar sua primeira obra de arte.", + "editor_tab_save": "Salvar", + "web_store": "Loja de temas", + "clear_themes": "Limpar", + "create_theme": "Criar", + "create_theme_modal_title": "Criar tema customizado", + "create_theme_modal_description": "Criar novo tema para customizar a aparência do Hydra", + "theme_name": "Nome", + "insert_theme_name": "Insira o nome do tema", + "set_theme": "Habilitar tema", + "unset_theme": "Desabilitar tema", + "delete_theme": "Deletar tema", + "edit_theme": "Editar tema", + "delete_all_themes": "Deletar todos os temas", + "delete_all_themes_description": "Isso irá deletar todos os seus temas", + "delete_theme_description": "Isso irá deletar o tema {{theme}}", + "cancel": "Cancelar", + "appearance": "Aparência", + "enable_torbox": "Habilitar TorBox", + "torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.", + "torbox_account_linked": "Conta do TorBox vinculada", + "create_real_debrid_account": "Clique aqui se você ainda não tem uma conta do Real-Debrid", + "create_torbox_account": "Clique aqui se você ainda não tem uma conta do TorBox", + "real_debrid_account_linked": "Conta Real-Debrid associada", + "name_min_length": "O nome do tema deve ter pelo menos 3 caracteres", + "import_theme": "Importar tema", + "import_theme_description": "Você irá importar {{theme}} da loja de temas", + "error_importing_theme": "Erro ao importar tema", + "theme_imported": "Tema importado com sucesso", + "enable_friend_request_notifications": "Quando um pedido de amizade é recebido", + "enable_auto_install": "Baixar atualizações automaticamente", + "common_redist": "Componentes recomendados", + "common_redist_description": "Componentes recomendados são necessários para executar alguns jogos. A instalação deles é recomendada para evitar problemas.", + "install_common_redist": "Instalar", + "installing_common_redist": "Instalando…", + "show_download_speed_in_megabytes": "Exibir taxas de download em megabytes por segundo", + "extract_files_by_default": "Extrair arquivos automaticamente após o download", + "enable_steam_achievements": "Habilitar busca por conquistas da Steam", + "enable_achievement_custom_notifications": "Habilitar notificações customizadas de conquistas", + "top-left": "Superior esquerdo", + "top-center": "Superior central", + "top-right": "Superior direito", + "bottom-left": "Inferior esquerdo", + "bottom-right": "Inferior direito", + "bottom-center": "Inferior central", + "achievement_custom_notification_position": "Posição das notificações customizadas de conquista", + "alignment": "Alinhamento", + "variation": "Variação", + "default": "Padrão", + "rare": "Rara", + "platinum": "Platina", + "hidden": "Oculta", + "test_notification": "Testar notificação", + "notification_preview": "Prévia da Notificação de Conquistas", + "enable_friend_start_game_notifications": "Quando um amigo iniciar um jogo" + }, + "notifications": { + "download_complete": "Download concluído", + "game_ready_to_install": "{{title}} está pronto para ser instalado", + "repack_list_updated": "Lista de repacks atualizada", + "repack_count_one": "{{count}} novo repack", + "repack_count_other": "{{count}} novos repacks", + "new_update_available": "Versão {{version}} disponível", + "restart_to_install_update": "Reinicie o Hydra para instalar a nova versão", + "new_friend_request_title": "Novo pedido de amizade", + "new_friend_request_description": "{{displayName}} te enviou um pedido de amizade", + "extraction_complete": "Extração concluída", + "game_extracted": "{{title}} extraído com sucesso", + "friend_started_playing_game": "{{displayName}} começou a jogar", + "test_achievement_notification_title": "Esta é uma notificação de teste", + "test_achievement_notification_description": "Bem legal, né?" + }, + "system_tray": { + "open": "Abrir Hydra", + "quit": "Fechar" + }, + "game_card": { + "available_one": "Disponível", + "available_other": "Disponíveis", + "no_downloads": "Sem downloads disponíveis" + }, + "binary_not_found_modal": { + "title": "Programas não instalados", + "description": "Os executáveis do Wine ou Lutris não foram encontrados em seu sistema.", + "instructions": "Verifique a forma correta de instalar algum deles no seu distro Linux, garantindo assim a execução normal do jogo" + }, + "catalogue": { + "search": "Filtrar…", + "developers": "Desenvolvedores", + "genres": "Gêneros", + "tags": "Marcadores", + "publishers": "Distribuidoras", + "download_sources": "Fontes de download", + "result_count": "{{resultCount}} resultados", + "filter_count": "{{filterCount}} disponíveis", + "clear_filters": "Limpar {{filterCount}} selecionados" + }, + "modal": { + "close": "Botão de fechar" + }, + "forms": { + "toggle_password_visibility": "Alternar visibilidade da senha" + }, + "user_profile": { + "amount_hours": "{{amount}} horas", + "amount_minutes": "{{amount}} minutos", + "last_time_played": "Última sessão {{period}}", + "activity": "Atividades recentes", + "library": "Biblioteca", + "total_play_time": "Tempo total de jogo", + "no_recent_activity_title": "Hmmm… nada por aqui", + "no_recent_activity_description": "Parece que você não jogou nada recentemente. Que tal começar agora?", + "display_name": "Nome de exibição", + "saving": "Salvando…", + "save": "Salvar", + "edit_profile": "Editar perfil", + "saved_successfully": "Salvo com sucesso", + "try_again": "Por favor, tente novamente", + "cancel": "Cancelar", + "successfully_signed_out": "Deslogado com sucesso", + "sign_out": "Sair da conta", + "sign_out_modal_title": "Deseja mesmo sair?", + "playing_for": "Jogando por {{amount}}", + "sign_out_modal_text": "Sua biblioteca de jogos está associada com a sua conta atual. Ao sair, sua biblioteca não aparecerá mais no Hydra e qualquer progresso não será salvo. Deseja continuar?", + "add_friends": "Adicionar Amigos", + "friend_code": "Código de amigo", + "see_profile": "Ver perfil", + "friend_request_sent": "Pedido de amizade enviado", + "friends": "Amigos", + "add": "Adicionar", + "sending": "Enviando", + "friends_list": "Lista de amigos", + "user_not_found": "Usuário não encontrado", + "block_user": "Bloquear", + "add_friend": "Adicionar amigo", + "request_sent": "Pedido enviado", + "request_received": "Pedido recebido", + "accept_request": "Aceitar pedido", + "ignore_request": "Ignorar pedido", + "cancel_request": "Cancelar pedido", + "undo_friendship": "Desfazer amizade", + "request_accepted": "Pedido de amizade aceito", + "user_blocked_successfully": "Usuário bloqueado com sucesso", + "user_block_modal_text": "Bloquear {{displayName}}", + "blocked_users": "Usuários bloqueados", + "unblock": "Desbloquear", + "no_friends_added": "Você ainda não possui amigos adicionados", + "pending": "Pendentes", + "no_pending_invites": "Você não possui convites de amizade pendentes", + "no_blocked_users": "Você não tem nenhum usuário bloqueado", + "friend_code_copied": "Código de amigo copiado", + "undo_friendship_modal_text": "Isso irá remover sua amizade com {{displayName}}", + "privacy_hint": "Pra controlar quem pode ver seu perfil, acesse a <0>Tela de Configurações", + "image_process_failure": "Falha ao processar a imagem", + "required_field": "Este campo é obrigatório", + "displayname_min_length": "Nome de exibição deve ter pelo menos 3 caracteres", + "displayname_max_length": "Nome de exibição deve ter no máximo 50 caracteres", + "locked_profile": "Este perfil é privado", + "report_profile": "Reportar este perfil", + "report_reason": "Por que você deseja reportar este perfil?", + "report_description": "Informações adicionais", + "report_description_placeholder": "Insira aqui", + "report": "Reportar", + "report_reason_hate": "Discurso de ódio", + "report_reason_sexual_content": "Conteúdo sexual", + "report_reason_violence": "Violência", + "report_reason_spam": "Spam", + "report_reason_other": "Outro", + "profile_reported": "Perfil reportado", + "your_friend_code": "Seu código de amigo:", + "upload_banner": "Carregar banner", + "uploading_banner": "Carregando banner…", + "background_image_updated": "Imagem de fundo salva", + "stats": "Estatísticas", + "achievements": "conquistas", + "games": "Jogos", + "ranking_updated_weekly": "O ranking é atualizado semanalmente", + "playing": "Jogando {{game}}", + "achievements_unlocked": "Conquistas desbloqueadas", + "earned_points": "Pontos ganhos", + "show_achievements_on_profile": "Exiba suas conquistas no perfil", + "show_points_on_profile": "Exiba seus pontos ganhos no perfil", + "error_adding_friend": "Não foi possível enviar o pedido de amizade. Verifique o código de amizade inserido", + "friend_code_length_error": "Código de amigo deve ter 8 caracteres" + }, + "achievement": { + "achievement_unlocked": "Conquista desbloqueada", + "your_achievements": "Suas Conquistas", + "user_achievements": "Conquistas de {{displayName}}", + "unlocked_at": "Desbloqueada em: {{date}}", + "subscription_needed": "Você precisa de uma assinatura Hydra Cloud para visualizar este conteúdo", + "new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos", + "achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas", + "achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}", + "hidden_achievement_tooltip": "Esta é uma conquista oculta", + "achievement_earn_points": "Ganhe {{points}} pontos com essa conquista", + "earned_points": "Pontos ganhos:", + "available_points": "Pontos disponíveis:", + "how_to_earn_achievements_points": "Como desbloquear pontos nas conquistas?" + }, + "hydra_cloud": { + "subscription_tour_title": "Assinatura Hydra Cloud", + "hydra_cloud": "Hydra Cloud", + "subscribe_now": "Inscreva-se agora", + "cloud_achievements": "Salvamento de conquistas em nuvem", + "animated_profile_picture": "Fotos de perfil animadas", + "premium_support": "Suporte Premium", + "show_and_compare_achievements": "Exiba e compare suas conquistas com outros usuários", + "animated_profile_banner": "Banner animado no perfil", + "cloud_saving": "Saves de jogos em nuvem", + "hydra_cloud_feature_found": "Você descobriu uma funcionalidade Hydra Cloud!", + "learn_more": "Saiba mais", + "debrid_description": "Baixe até 4x mais rápido com Nimbus" + } +} diff --git a/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx b/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx new file mode 100755 index 00000000..8d98f213 --- /dev/null +++ b/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx @@ -0,0 +1,55 @@ +import { useContext } from "react"; +import { useTranslation } from "react-i18next"; +import { gameDetailsContext } from "@renderer/context/game-details/game-details.context"; +import { SidebarSection } from "../sidebar-section/sidebar-section"; + +export function GameLanguageSection() { + const { t } = useTranslation("game_details"); + const { shopDetails, objectId } = useContext(gameDetailsContext); + + const getLanguages = () => { + let languages = shopDetails?.supported_languages; + if (!languages) return []; + languages = languages?.split('
')[0]; + let arrayIdiomas = languages?.split(',') + let listLanguages: { language: string; caption: string; audio: string }[] = []; + arrayIdiomas?.forEach((lang) => { + const objectLanguage = { + language: lang.replace("*", ""), + caption: "✔", + audio: lang.includes("*") ? "✔" : "" }; + listLanguages.push(objectLanguage); + }) + return listLanguages; + } + + + return ( + +
+

{t("supported_languages")}

+ + + + + + + + + + {getLanguages().map((lang) => ( + + + + + + ))} + +
{t("language")}{t("caption")}{t("audio")}
{lang.language}{lang.caption}{lang.audio}
+
+
+ Link Steam +
+
+ ); +} \ No newline at end of file diff --git a/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx b/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx new file mode 100755 index 00000000..3f7ff29c --- /dev/null +++ b/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx @@ -0,0 +1,62 @@ +import { useCallback, useContext, useEffect, useState } from "react"; +import { SidebarSection } from "../sidebar-section/sidebar-section"; +import { useTranslation } from "react-i18next"; +import { gameDetailsContext } from "@renderer/context/game-details/game-details.context"; +import { useAppSelector } from "@renderer/hooks"; + +export function GamePricesSection() { + const userPreferences = useAppSelector((state) => state.userPreferences.value); + const { t } = useTranslation("game_details"); + const [priceData, setPriceData] = useState(null); + const [isLoadingPrices, setIsLoadingPrices] = useState(false); + const { objectId } = useContext(gameDetailsContext); + + const fetchGamePrices = useCallback(async (steamAppId: string) => { + setIsLoadingPrices(true); + try { + const apiKey = userPreferences?.ggDealsApiKey || import.meta.env.VITE_GG_DEALS_API_KEY; + if (!apiKey) { + setPriceData(null); + setIsLoadingPrices(false); + return; + } + const url = `${import.meta.env.VITE_GG_DEALS_API_URL}/?ids=${steamAppId}&key=${apiKey}®ion=br`; + const response = await fetch(url); + if (!response.ok) { + throw new Error("Network response was not ok"); + } + const data = await response.json(); + setPriceData(data.data?.[steamAppId] ?? null); + } catch (error) { + setPriceData(null); + } finally { + setIsLoadingPrices(false); + } + }, []); + + useEffect(() => { + if (objectId) { + fetchGamePrices(objectId.toString()); + } + }, [objectId, fetchGamePrices]); + + return ( + + {isLoadingPrices ? ( +
{t("loading")}
+ ) : priceData ? ( +
+
    +
  • {t("retail_price")}: {t("currency_symbol")}{priceData.prices.currentRetail}
  • +
  • {t("keyshop_price")}: {t("currency_symbol")}{priceData.prices.currentKeyshops}
  • +
  • {t("historical_retail")}: {t("currency_symbol")}{priceData.prices.historicalRetail}
  • +
  • {t("historical_keyshop")}: {t("currency_symbol")}{priceData.prices.historicalKeyshops}
  • +
  • clique para ver todos os preços
  • +
+
+ ) : ( +
{t("no_prices_found")}
+ )} +
+ ); +} \ No newline at end of file diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.scss b/src/renderer/src/pages/game-details/sidebar/sidebar.scss old mode 100644 new mode 100755 index 783449fd..1f25b151 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.scss +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.scss @@ -1,196 +1,217 @@ -@use "../../../scss/globals.scss"; - -.content-sidebar { - border-left: solid 1px globals.$border-color; - background-color: globals.$dark-background-color; - width: 100%; - height: 100%; - - @media (min-width: 1024px) { - max-width: 300px; - width: 100%; - } - - @media (min-width: 1280px) { - width: 100%; - max-width: 400px; - } -} - -.requirement { - &__button-container { - width: 100%; - display: flex; - } - - &__button { - border: solid 1px globals.$border-color; - border-left: none; - border-right: none; - border-radius: 0; - width: 100%; - } - - &__details { - padding: calc(globals.$spacing-unit * 2); - line-height: 22px; - font-size: globals.$body-font-size; - - a { - display: flex; - color: globals.$body-color; - } - } - - &__details-skeleton { - display: flex; - flex-direction: column; - gap: globals.$spacing-unit; - padding: calc(globals.$spacing-unit * 2); - font-size: globals.$body-font-size; - } -} - -.how-long-to-beat { - &__categories-list { - margin: 0; - padding: calc(globals.$spacing-unit * 2); - display: flex; - flex-direction: column; - gap: calc(globals.$spacing-unit * 2); - } - - &__category { - display: flex; - flex-direction: column; - gap: calc(globals.$spacing-unit / 2); - background: linear-gradient( - 90deg, - transparent 20%, - rgb(255 255 255 / 2%) 100% - ); - border-radius: 4px; - padding: globals.$spacing-unit calc(globals.$spacing-unit * 2); - border: solid 1px globals.$border-color; - } - - &__category-label { - color: globals.$muted-color; - } - - &__category-label--bold { - font-weight: bold; - } - - &__category-skeleton { - border: solid 1px globals.$border-color; - border-radius: 4px; - height: 76px; - } -} - -.stats { - &__section { - display: flex; - gap: calc(globals.$spacing-unit * 2); - padding: calc(globals.$spacing-unit * 2); - justify-content: space-between; - transition: max-height ease 0.5s; - overflow: hidden; - - @media (min-width: 1024px) { - flex-direction: column; - } - - @media (min-width: 1280px) { - flex-direction: row; - } - } - - &__category-title { - font-size: globals.$small-font-size; - font-weight: bold; - display: flex; - align-items: center; - gap: globals.$spacing-unit; - } - - &__category { - display: flex; - flex-direction: row; - gap: calc(globals.$spacing-unit / 2); - justify-content: space-between; - align-items: center; - } -} - -.list { - list-style: none; - margin: 0; - display: flex; - flex-direction: column; - gap: calc(globals.$spacing-unit * 2); - padding: calc(globals.$spacing-unit * 2); - - &__item { - display: flex; - cursor: pointer; - transition: all ease 0.1s; - color: globals.$muted-color; - width: 100%; - overflow: hidden; - border-radius: 4px; - padding: globals.$spacing-unit; - gap: calc(globals.$spacing-unit * 2); - align-items: center; - text-align: left; - - &:hover { - background-color: rgba(255, 255, 255, 0.15); - text-decoration: none; - } - } - - &__item-image { - width: 54px; - height: 54px; - border-radius: 4px; - object-fit: cover; - - &--locked { - filter: grayscale(100%); - } - } -} - -.subscription-required-button { - text-decoration: none; - display: flex; - justify-content: center; - width: 100%; - gap: calc(globals.$spacing-unit / 2); - color: globals.$warning-color; - cursor: pointer; - - &:hover { - text-decoration: underline; - } -} - -.achievements-placeholder { - position: absolute; - z-index: 1; - inset: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - gap: 8px; -} - -.achievements-placeholder__blur { - filter: blur(4px); -} +@use "../../../scss/globals.scss"; + +.content-sidebar { + border-left: solid 1px globals.$border-color; + background-color: globals.$dark-background-color; + width: 100%; + height: 100%; + + @media (min-width: 1024px) { + max-width: 300px; + width: 100%; + } + + @media (min-width: 1280px) { + width: 100%; + max-width: 400px; + } +} + +.requirement { + &__button-container { + width: 100%; + display: flex; + } + + &__button { + border: solid 1px globals.$border-color; + border-left: none; + border-right: none; + border-radius: 0; + width: 100%; + } + + &__details { + padding: calc(globals.$spacing-unit * 2); + line-height: 22px; + font-size: globals.$body-font-size; + + a { + display: flex; + color: globals.$body-color; + } + } + + &__details-skeleton { + display: flex; + flex-direction: column; + gap: globals.$spacing-unit; + padding: calc(globals.$spacing-unit * 2); + font-size: globals.$body-font-size; + } +} + +.how-long-to-beat { + &__categories-list { + margin: 0; + padding: calc(globals.$spacing-unit * 2); + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit * 2); + } + + &__category { + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit / 2); + background: linear-gradient( + 90deg, + transparent 20%, + rgb(255 255 255 / 2%) 100% + ); + border-radius: 4px; + padding: globals.$spacing-unit calc(globals.$spacing-unit * 2); + border: solid 1px globals.$border-color; + } + + &__category-label { + color: globals.$muted-color; + } + + &__category-label--bold { + font-weight: bold; + } + + &__category-skeleton { + border: solid 1px globals.$border-color; + border-radius: 4px; + height: 76px; + } +} + +.stats { + &__section { + display: flex; + gap: calc(globals.$spacing-unit * 2); + padding: calc(globals.$spacing-unit * 2); + justify-content: space-between; + transition: max-height ease 0.5s; + overflow: hidden; + + @media (min-width: 1024px) { + flex-direction: column; + } + + @media (min-width: 1280px) { + flex-direction: row; + } + } + + &__category-title { + font-size: globals.$small-font-size; + font-weight: bold; + display: flex; + align-items: center; + gap: globals.$spacing-unit; + } + + &__category { + display: flex; + flex-direction: row; + gap: calc(globals.$spacing-unit / 2); + justify-content: space-between; + align-items: center; + } +} + +.list { + list-style: none; + margin: 0; + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit * 2); + padding: calc(globals.$spacing-unit * 2); + + &__item { + display: flex; + cursor: pointer; + transition: all ease 0.1s; + color: globals.$muted-color; + width: 100%; + overflow: hidden; + border-radius: 4px; + padding: globals.$spacing-unit; + gap: calc(globals.$spacing-unit * 2); + align-items: center; + text-align: left; + + &:hover { + background-color: rgba(255, 255, 255, 0.15); + text-decoration: none; + } + } + + &__item-image { + width: 54px; + height: 54px; + border-radius: 4px; + object-fit: cover; + + &--locked { + filter: grayscale(100%); + } + } +} + +.subscription-required-button { + text-decoration: none; + display: flex; + justify-content: center; + width: 100%; + gap: calc(globals.$spacing-unit / 2); + color: globals.$warning-color; + cursor: pointer; + + &:hover { + text-decoration: underline; + } +} + +.achievements-placeholder { + position: absolute; + z-index: 1; + inset: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + gap: 8px; +} + +.achievements-placeholder__blur { + filter: blur(4px); +} + +.table-languages { + width: 100%; + border-collapse: collapse; + text-align: left; + + th, td { + padding: globals.$spacing-unit; + border-bottom: solid 1px globals.$border-color; + } + + th { + font-size: globals.$small-font-size; + color: globals.$muted-color; + font-weight: normal; + } + + td { + font-size: globals.$body-font-size; + } +} \ No newline at end of file diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx old mode 100644 new mode 100755 index 7e107810..e52a7126 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx @@ -1,268 +1,273 @@ -import { useContext, useEffect, useState } from "react"; -import type { - HowLongToBeatCategory, - SteamAppDetails, - UserAchievement, -} from "@types"; -import { useTranslation } from "react-i18next"; -import { Button, Link } from "@renderer/components"; - -import { gameDetailsContext } from "@renderer/context"; -import { useDate, useFormat, useUserDetails } from "@renderer/hooks"; -import { - CloudOfflineIcon, - DownloadIcon, - LockIcon, - PeopleIcon, -} from "@primer/octicons-react"; -import { HowLongToBeatSection } from "./how-long-to-beat-section"; -import { howLongToBeatEntriesTable } from "@renderer/dexie"; -import { SidebarSection } from "../sidebar-section/sidebar-section"; -import { buildGameAchievementPath } from "@renderer/helpers"; -import { useSubscription } from "@renderer/hooks/use-subscription"; -import "./sidebar.scss"; - -const achievementsPlaceholder: UserAchievement[] = [ - { - displayName: "Timber!!", - name: "1", - hidden: false, - description: "Chop down your first tree.", - icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0fbb33098c9da39d1d4771d8209afface9c46e81.jpg", - icongray: - "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0fbb33098c9da39d1d4771d8209afface9c46e81.jpg", - unlocked: true, - unlockTime: Date.now(), - }, - { - displayName: "Supreme Helper Minion!", - name: "2", - hidden: false, - icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0a6ff6a36670c96ceb4d30cf6fd69d2fdf55f38e.jpg", - icongray: - "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0a6ff6a36670c96ceb4d30cf6fd69d2fdf55f38e.jpg", - unlocked: false, - unlockTime: null, - }, - { - displayName: "Feast of Midas", - name: "3", - hidden: false, - icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/2d10311274fe7c92ab25cc29afdca86b019ad472.jpg", - icongray: - "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/2d10311274fe7c92ab25cc29afdca86b019ad472.jpg", - unlocked: false, - unlockTime: null, - }, -]; - -export function Sidebar() { - const [howLongToBeat, setHowLongToBeat] = useState<{ - isLoading: boolean; - data: HowLongToBeatCategory[] | null; - }>({ isLoading: true, data: null }); - - const { userDetails, hasActiveSubscription } = useUserDetails(); - const [activeRequirement, setActiveRequirement] = - useState("minimum"); - - const { gameTitle, shopDetails, objectId, shop, stats, achievements } = - useContext(gameDetailsContext); - - const { showHydraCloudModal } = useSubscription(); - const { t } = useTranslation("game_details"); - const { formatDateTime } = useDate(); - const { numberFormatter } = useFormat(); - - useEffect(() => { - if (objectId) { - setHowLongToBeat({ isLoading: true, data: null }); - - howLongToBeatEntriesTable - .where({ shop, objectId }) - .first() - .then(async (cachedHowLongToBeat) => { - if (cachedHowLongToBeat) { - setHowLongToBeat({ - isLoading: false, - data: cachedHowLongToBeat.categories, - }); - } else { - try { - const howLongToBeat = await window.electron.getHowLongToBeat( - objectId, - shop - ); - - if (howLongToBeat) { - howLongToBeatEntriesTable.add({ - objectId, - shop: "steam", - createdAt: new Date(), - updatedAt: new Date(), - categories: howLongToBeat, - }); - } - - setHowLongToBeat({ isLoading: false, data: howLongToBeat }); - } catch (err) { - setHowLongToBeat({ isLoading: false, data: null }); - } - } - }); - } - }, [objectId, shop, gameTitle]); - - return ( - - ); -} +import { useContext, useEffect, useState } from "react"; +import type { + HowLongToBeatCategory, + SteamAppDetails, + UserAchievement, +} from "@types"; +import { useTranslation } from "react-i18next"; +import { Button, Link } from "@renderer/components"; + +import { gameDetailsContext } from "@renderer/context"; +import { useDate, useFormat, useUserDetails } from "@renderer/hooks"; +import { + CloudOfflineIcon, + DownloadIcon, + LockIcon, + PeopleIcon, +} from "@primer/octicons-react"; +import { HowLongToBeatSection } from "./how-long-to-beat-section"; +import { howLongToBeatEntriesTable } from "@renderer/dexie"; +import { SidebarSection } from "../sidebar-section/sidebar-section"; +import { buildGameAchievementPath } from "@renderer/helpers"; +import { useSubscription } from "@renderer/hooks/use-subscription"; +import "./sidebar.scss"; +import { GamePricesSection } from "./game-prices-section"; +import { GameLanguageSection } from "./game-language-section"; + +const achievementsPlaceholder: UserAchievement[] = [ + { + displayName: "Timber!!", + name: "1", + hidden: false, + description: "Chop down your first tree.", + icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0fbb33098c9da39d1d4771d8209afface9c46e81.jpg", + icongray: + "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0fbb33098c9da39d1d4771d8209afface9c46e81.jpg", + unlocked: true, + unlockTime: Date.now(), + }, + { + displayName: "Supreme Helper Minion!", + name: "2", + hidden: false, + icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0a6ff6a36670c96ceb4d30cf6fd69d2fdf55f38e.jpg", + icongray: + "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0a6ff6a36670c96ceb4d30cf6fd69d2fdf55f38e.jpg", + unlocked: false, + unlockTime: null, + }, + { + displayName: "Feast of Midas", + name: "3", + hidden: false, + icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/2d10311274fe7c92ab25cc29afdca86b019ad472.jpg", + icongray: + "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/2d10311274fe7c92ab25cc29afdca86b019ad472.jpg", + unlocked: false, + unlockTime: null, + }, +]; + +export function Sidebar() { + const [howLongToBeat, setHowLongToBeat] = useState<{ + isLoading: boolean; + data: HowLongToBeatCategory[] | null; + }>({ isLoading: true, data: null }); + + const { userDetails, hasActiveSubscription } = useUserDetails(); + const [activeRequirement, setActiveRequirement] = + useState("minimum"); + + const { gameTitle, shopDetails, objectId, shop, stats, achievements } = + useContext(gameDetailsContext); + + const { showHydraCloudModal } = useSubscription(); + const { t } = useTranslation("game_details"); + const { formatDateTime } = useDate(); + const { numberFormatter } = useFormat(); + + useEffect(() => { + if (objectId) { + setHowLongToBeat({ isLoading: true, data: null }); + + howLongToBeatEntriesTable + .where({ shop, objectId }) + .first() + .then(async (cachedHowLongToBeat) => { + if (cachedHowLongToBeat) { + setHowLongToBeat({ + isLoading: false, + data: cachedHowLongToBeat.categories, + }); + } else { + try { + const howLongToBeat = await window.electron.getHowLongToBeat( + objectId, + shop + ); + + if (howLongToBeat) { + howLongToBeatEntriesTable.add({ + objectId, + shop: "steam", + createdAt: new Date(), + updatedAt: new Date(), + categories: howLongToBeat, + }); + } + + setHowLongToBeat({ isLoading: false, data: howLongToBeat }); + } catch (err) { + setHowLongToBeat({ isLoading: false, data: null }); + } + } + }); + } + }, [objectId, shop, gameTitle]); + + return ( + + ); +} diff --git a/src/renderer/src/pages/settings/settings-general.tsx b/src/renderer/src/pages/settings/settings-general.tsx index c698440d..b952dfa0 100644 --- a/src/renderer/src/pages/settings/settings-general.tsx +++ b/src/renderer/src/pages/settings/settings-general.tsx @@ -35,6 +35,7 @@ export function SettingsGeneral() { const [form, setForm] = useState({ downloadsPath: "", + ggDealsApiKey: "", downloadNotificationsEnabled: false, repackUpdatesNotificationsEnabled: false, friendRequestNotificationsEnabled: false, @@ -100,6 +101,7 @@ export function SettingsGeneral() { setForm((prev) => ({ ...prev, downloadsPath: userPreferences.downloadsPath ?? defaultDownloadsPath, + ggDealsApiKey: userPreferences.ggDealsApiKey ?? "", downloadNotificationsEnabled: userPreferences.downloadNotificationsEnabled ?? false, repackUpdatesNotificationsEnabled: @@ -206,6 +208,12 @@ export function SettingsGeneral() { } /> + handleChange({ ggDealsApiKey: e.target.value })} + /> + Date: Sun, 7 Sep 2025 12:37:49 -0300 Subject: [PATCH 02/27] feat: Added the game's supported languages. Added the game's lowest historical price and a link to see the price in various stores (keyshop and official store) --- src/locales/en/translation.json | 1132 ++++++++--------- src/locales/pt-BR/translation.json | 1118 ++++++++-------- .../sidebar/game-language-section.tsx | 121 +- .../sidebar/game-prices-section.tsx | 148 ++- .../pages/game-details/sidebar/sidebar.scss | 435 +++---- .../pages/game-details/sidebar/sidebar.tsx | 546 ++++---- 6 files changed, 1768 insertions(+), 1732 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index a2a2372b..26b44e58 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -1,566 +1,566 @@ -{ - "language_name": "English", - "app": { - "successfully_signed_in": "Successfully signed in" - }, - "home": { - "featured": "Featured", - "surprise_me": "Surprise me", - "no_results": "No results found", - "start_typing": "Starting typing to search...", - "hot": "Hot now", - "weekly": "📅 Top games of the week", - "achievements": "🏆 Games to beat" - }, - "sidebar": { - "catalogue": "Catalogue", - "downloads": "Downloads", - "settings": "Settings", - "my_library": "My library", - "downloading_metadata": "{{title}} (Downloading metadata…)", - "paused": "{{title}} (Paused)", - "downloading": "{{title}} ({{percentage}} - Downloading…)", - "filter": "Filter library", - "home": "Home", - "queued": "{{title}} (Queued)", - "game_has_no_executable": "Game has no executable selected", - "sign_in": "Sign in", - "friends": "Friends", - "need_help": "Need help?", - "favorites": "Favorites", - "playable_button_title": "Show only games you can play now" - }, - "header": { - "search": "Search games", - "home": "Home", - "catalogue": "Catalogue", - "downloads": "Downloads", - "search_results": "Search results", - "settings": "Settings", - "version_available_install": "Version {{version}} available. Click here to restart and install.", - "version_available_download": "Version {{version}} available. Click here to download." - }, - "bottom_panel": { - "no_downloads_in_progress": "No downloads in progress", - "downloading_metadata": "Downloading {{title}} metadata…", - "downloading": "Downloading {{title}}… ({{percentage}} complete) - Completion {{eta}} - {{speed}}", - "calculating_eta": "Downloading {{title}}… ({{percentage}} complete) - Calculating remaining time…", - "checking_files": "Checking {{title}} files… ({{percentage}} complete)", - "installing_common_redist": "{{log}}…", - "installation_complete": "Installation complete", - "installation_complete_message": "Common redistributables installed successfully" - }, - "catalogue": { - "search": "Filter…", - "developers": "Developers", - "genres": "Genres", - "tags": "Tags", - "publishers": "Publishers", - "download_sources": "Download sources", - "result_count": "{{resultCount}} results", - "filter_count": "{{filterCount}} available", - "clear_filters": "Clear {{filterCount}} selected" - }, - "game_details": { - "open_download_options": "Open download options", - "download_options_zero": "No download option", - "download_options_one": "{{count}} download option", - "download_options_other": "{{count}} download options", - "updated_at": "Updated {{updated_at}}", - "install": "Install", - "resume": "Resume", - "pause": "Pause", - "cancel": "Cancel", - "remove": "Remove", - "space_left_on_disk": "{{space}} left on disk", - "eta": "Conclusion {{eta}}", - "calculating_eta": "Calculating remaining time…", - "downloading_metadata": "Downloading metadata…", - "filter": "Filter repacks", - "requirements": "System requirements", - "minimum": "Minimum", - "recommended": "Recommended", - "paused": "Paused", - "release_date": "Released on {{date}}", - "publisher": "Published by {{publisher}}", - "hours": "hours", - "minutes": "minutes", - "amount_hours": "{{amount}} hours", - "amount_minutes": "{{amount}} minutes", - "accuracy": "{{accuracy}}% accuracy", - "add_to_library": "Add to library", - "already_in_library": "Already in library", - "remove_from_library": "Remove from library", - "no_downloads": "No downloads available", - "play_time": "Played for {{amount}}", - "last_time_played": "Last played {{period}}", - "not_played_yet": "You haven't played {{title}} yet", - "next_suggestion": "Next suggestion", - "play": "Play", - "deleting": "Deleting installer…", - "close": "Close", - "playing_now": "Playing now", - "change": "Change", - "repacks_modal_description": "Choose the repack you want to download", - "select_folder_hint": "To change the default folder, go to the <0>Settings", - "download_now": "Download now", - "no_shop_details": "Could not retrieve shop details.", - "download_options": "Download options", - "download_path": "Download path", - "previous_screenshot": "Previous screenshot", - "next_screenshot": "Next screenshot", - "screenshot": "Screenshot {{number}}", - "open_screenshot": "Open screenshot {{number}}", - "download_settings": "Download settings", - "downloader": "Downloader", - "select_executable": "Select", - "no_executable_selected": "No executable selected", - "open_folder": "Open folder", - "open_download_location": "See downloaded files", - "create_shortcut": "Create desktop shortcut", - "clear": "Clear", - "remove_files": "Remove files", - "remove_from_library_title": "Are you sure?", - "remove_from_library_description": "This will remove {{game}} from your library", - "options": "Options", - "executable_section_title": "Executable", - "executable_section_description": "Path of the file that will be executed when \"Play\" is clicked", - "downloads_section_title": "Downloads", - "downloads_section_description": "Check out updates or other versions of this game", - "danger_zone_section_title": "Danger zone", - "danger_zone_section_description": "Remove this game from your library or the files downloaded by Hydra", - "download_in_progress": "Download in progress", - "download_paused": "Download paused", - "last_downloaded_option": "Last downloaded option", - "create_steam_shortcut": "Create Steam shortcut", - "create_shortcut_success": "Shortcut created successfully", - "you_might_need_to_restart_steam": "You might need to restart Steam to see the changes", - "create_shortcut_error": "Error creating shortcut", - "nsfw_content_title": "This game contains inappropriate content", - "nsfw_content_description": "{{title}} contains content that may not be suitable for all ages. Are you sure you want to continue?", - "allow_nsfw_content": "Continue", - "refuse_nsfw_content": "Go back", - "stats": "Stats", - "download_count": "Downloads", - "player_count": "Active players", - "download_error": "This download option is not available", - "download": "Download", - "executable_path_in_use": "Executable already in use by \"{{game}}\"", - "warning": "Warning:", - "hydra_needs_to_remain_open": "for this download, Hydra needs to remain open util it's completed. If Hydra closes before completing, you will lose your progress.", - "achievements": "Achievements", - "achievements_count": "Achievements {{unlockedCount}}/{{achievementsCount}}", - "cloud_save": "Cloud save", - "cloud_save_description": "Save your progress in the cloud and continue playing on any device", - "backups": "Backups", - "install_backup": "Install", - "delete_backup": "Delete", - "create_backup": "New backup", - "last_backup_date": "Last backup on {{date}}", - "no_backup_preview": "No save games were found for this title", - "restoring_backup": "Restoring backup ({{progress}} complete)…", - "uploading_backup": "Uploading backup…", - "no_backups": "You haven't created any backups for this game yet", - "backup_uploaded": "Backup uploaded", - "backup_deleted": "Backup deleted", - "backup_restored": "Backup restored", - "see_all_achievements": "See all achievements", - "sign_in_to_see_achievements": "Sign in to see achievements", - "mapping_method_automatic": "Automatic", - "mapping_method_manual": "Manual", - "mapping_method_label": "Mapping method", - "files_automatically_mapped": "Files automatically mapped", - "no_backups_created": "No backups created for this game", - "manage_files": "Manage files", - "loading_save_preview": "Searching for save games…", - "wine_prefix": "Wine Prefix", - "wine_prefix_description": "The Wine prefix used to run this game", - "launch_options": "Launch Options", - "launch_options_description": "Advanced users may choose to enter modifications to their launch options (experimental feature)", - "launch_options_placeholder": "No parameter specified", - "no_download_option_info": "No information available", - "backup_deletion_failed": "Failed to delete backup", - "max_number_of_artifacts_reached": "Maximum number of backups reached for this game", - "achievements_not_sync": "See how to synchronize your achievements", - "manage_files_description": "Manage which files will be backed up and restored", - "select_folder": "Select folder", - "backup_from": "Backup from {{date}}", - "automatic_backup_from": "Automatic backup from {{date}}", - "enable_automatic_cloud_sync": "Enable automatic cloud sync", - "custom_backup_location_set": "Custom backup location set", - "no_directory_selected": "No directory selected", - "no_write_permission": "Cannot download into this directory. Click here to learn more.", - "reset_achievements": "Reset achievements", - "reset_achievements_description": "This will reset all achievements for {{game}}", - "reset_achievements_title": "Are you sure?", - "reset_achievements_success": "Achievements successfully reset", - "reset_achievements_error": "Failed to reset achievements", - "download_error_gofile_quota_exceeded": "You have exceeded your Gofile monthly quota. Please await the quota to reset.", - "download_error_real_debrid_account_not_authorized": "Your Real-Debrid account is not authorized to make new downloads. Please check your account settings and try again.", - "download_error_not_cached_on_real_debrid": "This download is not available on Real-Debrid and polling download status from Real-Debrid is not yet available.", - "download_error_not_cached_on_torbox": "This download is not available on TorBox and polling download status from TorBox is not yet available.", - "download_error_not_cached_on_hydra": "This download is not available on Nimbus.", - "game_removed_from_favorites": "Game removed from favorites", - "game_added_to_favorites": "Game added to favorites", - "automatically_extract_downloaded_files": "Automatically extract downloaded files", - "create_start_menu_shortcut": "Create Start Menu shortcut", - "invalid_wine_prefix_path": "Invalid Wine prefix path", - "invalid_wine_prefix_path_description": "The path to the Wine prefix is invalid. Please check the path and try again.", - "missing_wine_prefix": "Wine prefix is required to create a backup on Linux", - "artifact_renamed": "Backup renamed successfully", - "rename_artifact": "Rename Backup", - "rename_artifact_description": "Rename the backup to a more descriptive name", - "artifact_name_label": "Backup name", - "artifact_name_placeholder": "Enter a name for the backup", - "save_changes": "Save changes", - "required_field": "This field is required", - "max_length_field": "This field must be less than {{length}} characters", - "freeze_backup": "Pin it so it's not overwritten by automatic backups", - "unfreeze_backup": "Unpin it", - "backup_frozen": "Backup pinned", - "backup_unfrozen": "Backup unpinned", - "backup_freeze_failed": "Failed to freeze backup", - "backup_freeze_failed_description": "You must leave at least one free slot for automatic backups", - "game_details": "Game Details", - "currency_symbol": "$", - "currency_country": "us", - "prices": "Prices", - "no_prices_found": "No prices found", - "retail_price": "Retail price", - "keyshop_price": "Keyshop price", - "historical_retail": "Historical retail", - "historical_keyshop": "Historical keyshop", - "supported_languages": "Supported languages", - "language": "Language", - "caption": "Caption", - "audio": "Audio" - }, - "activation": { - "title": "Activate Hydra", - "installation_id": "Installation ID:", - "enter_activation_code": "Enter your activation code", - "message": "If you don't know where to ask for this, then you shouldn't have this.", - "activate": "Activate", - "loading": "Loading…" - }, - "downloads": { - "resume": "Resume", - "pause": "Pause", - "eta": "Conclusion {{eta}}", - "paused": "Paused", - "verifying": "Verifying…", - "completed": "Completed", - "removed": "Not downloaded", - "cancel": "Cancel", - "filter": "Filter downloaded games", - "remove": "Remove", - "downloading_metadata": "Downloading metadata…", - "deleting": "Deleting installer…", - "delete": "Remove installer", - "delete_modal_title": "Are you sure?", - "delete_modal_description": "This will remove all the installation files from your computer", - "install": "Install", - "download_in_progress": "In progress", - "queued_downloads": "Queued downloads", - "downloads_completed": "Completed", - "queued": "Queued", - "no_downloads_title": "Such empty", - "no_downloads_description": "You haven't downloaded anything with Hydra yet, but it's never too late to start.", - "checking_files": "Checking files…", - "seeding": "Seeding", - "stop_seeding": "Stop seeding", - "resume_seeding": "Resume seeding", - "options": "Manage", - "extract": "Extract files", - "extracting": "Extracting files…" - }, - "settings": { - "downloads_path": "Downloads path", - "change": "Update", - "notifications": "Notifications", - "enable_download_notifications": "When a download is complete", - "gg_deals_api_key_description": "gg deals api key. Used to show the lowest price. (https://gg.deals/api/)", - "enable_repack_list_notifications": "When a new repack is added", - "real_debrid_api_token_label": "Real-Debrid API token", - "quit_app_instead_hiding": "Don't hide Hydra when closing", - "launch_with_system": "Launch Hydra on system start-up", - "general": "General", - "behavior": "Behavior", - "download_sources": "Download sources", - "language": "Language", - "api_token": "API Token", - "enable_real_debrid": "Enable Real-Debrid", - "real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to quickly download files, only limited by your internet speed.", - "debrid_invalid_token": "Invalid API token", - "debrid_api_token_hint": "You can get your API token <0>here", - "real_debrid_free_account_error": "The account \"{{username}}\" is a free account. Please subscribe to Real-Debrid", - "debrid_linked_message": "Account \"{{username}}\" linked", - "save_changes": "Save changes", - "changes_saved": "Changes successfully saved", - "download_sources_description": "Hydra will fetch the download links from these sources. The source URL must be a direct link to a .json file containing the download links.", - "validate_download_source": "Validate", - "remove_download_source": "Remove", - "add_download_source": "Add source", - "download_count_zero": "No download options", - "download_count_one": "{{countFormatted}} download option", - "download_count_other": "{{countFormatted}} download options", - "download_source_url": "Download source URL", - "add_download_source_description": "Insert the URL of the .json file", - "download_source_up_to_date": "Up-to-date", - "download_source_errored": "Errored", - "sync_download_sources": "Sync sources", - "removed_download_source": "Download source removed", - "removed_download_sources": "Download sources removed", - "cancel_button_confirmation_delete_all_sources": "No", - "confirm_button_confirmation_delete_all_sources": "Yes, delete everything", - "title_confirmation_delete_all_sources": "Delete all download sources", - "description_confirmation_delete_all_sources": "You will delete all download sources", - "button_delete_all_sources": "Remove all", - "added_download_source": "Added download source", - "download_sources_synced": "All download sources are synced", - "insert_valid_json_url": "Insert a valid JSON url", - "found_download_option_zero": "No download option found", - "found_download_option_one": "Found {{countFormatted}} download option", - "found_download_option_other": "Found {{countFormatted}} download options", - "import": "Import", - "public": "Public", - "private": "Private", - "friends_only": "Friends only", - "privacy": "Privacy", - "profile_visibility": "Profile visibility", - "profile_visibility_description": "Choose who can see your profile and library", - "required_field": "This field is required", - "source_already_exists": "This source has already been added", - "must_be_valid_url": "The source must be a valid URL", - "blocked_users": "Blocked users", - "user_unblocked": "User has been unblocked", - "enable_achievement_notifications": "When an achievement is unlocked", - "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", - "account": "Account", - "no_users_blocked": "You have no blocked users", - "subscription_active_until": "Your Hydra Cloud is active until {{date}}", - "manage_subscription": "Manage subscription", - "update_email": "Update email", - "update_password": "Update password", - "current_email": "Current email:", - "no_email_account": "You have not set an email yet", - "account_data_updated_successfully": "Account data updated successfully", - "renew_subscription": "Renew Hydra Cloud", - "subscription_expired_at": "Your subscription expired at {{date}}", - "no_subscription": "Enjoy Hydra in the best possible way", - "become_subscriber": "Be Hydra Cloud", - "subscription_renew_cancelled": "Automatic renewal is disabled", - "subscription_renews_on": "Your subscription renews on {{date}}", - "bill_sent_until": "Your next bill will be sent until this day", - "no_themes": "Seems like you don't have any themes yet, but no worries, click here to create your first masterpiece.", - "editor_tab_code": "Code", - "editor_tab_info": "Info", - "editor_tab_save": "Save", - "web_store": "Web store", - "clear_themes": "Clear", - "create_theme": "Create", - "create_theme_modal_title": "Create custom theme", - "create_theme_modal_description": "Create a new theme to customize Hydra's appearance", - "theme_name": "Name", - "insert_theme_name": "Insert theme name", - "set_theme": "Set theme", - "unset_theme": "Unset theme", - "delete_theme": "Delete theme", - "edit_theme": "Edit theme", - "delete_all_themes": "Delete all themes", - "delete_all_themes_description": "This will delete all your custom themes", - "delete_theme_description": "This will delete the theme {{theme}}", - "cancel": "Cancel", - "appearance": "Appearance", - "enable_torbox": "Enable TorBox", - "torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.", - "torbox_account_linked": "TorBox account linked", - "create_real_debrid_account": "Click here if you don't have a Real-Debrid account yet", - "create_torbox_account": "Click here if you don't have a TorBox account yet", - "real_debrid_account_linked": "Real-Debrid account linked", - "name_min_length": "Theme name must be at least 3 characters long", - "import_theme": "Import theme", - "import_theme_description": "You will import {{theme}} from the theme store", - "error_importing_theme": "Error importing theme", - "theme_imported": "Theme imported successfully", - "enable_friend_request_notifications": "When a friend request is received", - "enable_auto_install": "Download updates automatically", - "common_redist": "Common redistributables", - "common_redist_description": "Common redistributables are required to run some games. Installing them is recommended to avoid issues.", - "install_common_redist": "Install", - "installing_common_redist": "Installing…", - "show_download_speed_in_megabytes": "Show download speed in megabytes per second", - "extract_files_by_default": "Extract files by default after download", - "enable_steam_achievements": "Enable search for Steam achievements", - "achievement_custom_notification_position": "Achievement custom notification position", - "top-left": "Top left", - "top-center": "Top center", - "top-right": "Top right", - "bottom-left": "Bottom left", - "bottom-center": "Bottom center", - "bottom-right": "Bottom right", - "enable_achievement_custom_notifications": "Enable achievement custom notifications", - "alignment": "Alignment", - "variation": "Variation", - "default": "Default", - "rare": "Rare", - "platinum": "Platinum", - "hidden": "Hidden", - "test_notification": "Test notification", - "notification_preview": "Achievement Notification Preview", - "enable_friend_start_game_notifications": "When a friend starts playing a game" - }, - "notifications": { - "download_complete": "Download complete", - "game_ready_to_install": "{{title}} is ready to install", - "repack_list_updated": "Repack list updated", - "repack_count_one": "{{count}} repack added", - "repack_count_other": "{{count}} repacks added", - "new_update_available": "Version {{version}} available", - "restart_to_install_update": "Restart Hydra to install the update", - "notification_achievement_unlocked_title": "Achievement unlocked for {{game}}", - "notification_achievement_unlocked_body": "{{achievement}} and other {{count}} were unlocked", - "new_friend_request_description": "{{displayName}} sent you a friend request", - "new_friend_request_title": "New friend request", - "extraction_complete": "Extraction complete", - "game_extracted": "{{title}} extracted successfully", - "friend_started_playing_game": "{{displayName}} started playing a game", - "test_achievement_notification_title": "This is a test notification", - "test_achievement_notification_description": "Pretty cool, huh?" - }, - "system_tray": { - "open": "Open Hydra", - "quit": "Quit" - }, - "game_card": { - "available_one": "Available", - "available_other": "Available", - "no_downloads": "No downloads available" - }, - "binary_not_found_modal": { - "title": "Programs not installed", - "description": "Wine or Lutris executables were not found on your system", - "instructions": "Check the correct way to install any of them on your Linux distro so that the game can run normally" - }, - "modal": { - "close": "Close button" - }, - "forms": { - "toggle_password_visibility": "Toggle password visibility" - }, - "user_profile": { - "amount_hours": "{{amount}} hours", - "amount_minutes": "{{amount}} minutes", - "last_time_played": "Last played {{period}}", - "activity": "Recent Activity", - "library": "Library", - "total_play_time": "Total playtime", - "no_recent_activity_title": "Hmmm… nothing here", - "no_recent_activity_description": "You haven't played any games recently. It's time to change that!", - "display_name": "Display name", - "saving": "Saving", - "save": "Save", - "edit_profile": "Edit Profile", - "saved_successfully": "Saved successfully", - "try_again": "Please, try again", - "sign_out_modal_title": "Are you sure?", - "cancel": "Cancel", - "successfully_signed_out": "Successfully signed out", - "sign_out": "Sign out", - "playing_for": "Playing for {{amount}}", - "sign_out_modal_text": "Your library is linked with your current account. When signing out, your library will not be visible anymore, and any progress will not be saved. Continue with sign out?", - "add_friends": "Add Friends", - "add": "Add", - "friend_code": "Friend code", - "see_profile": "See profile", - "sending": "Sending", - "friend_request_sent": "Friend request sent", - "friends": "Friends", - "friends_list": "Friends list", - "user_not_found": "User not found", - "block_user": "Block user", - "add_friend": "Add friend", - "request_sent": "Request sent", - "request_received": "Request received", - "accept_request": "Accept request", - "ignore_request": "Ignore request", - "cancel_request": "Cancel request", - "undo_friendship": "Undo friendship", - "request_accepted": "Request accepted", - "user_blocked_successfully": "User blocked successfully", - "user_block_modal_text": "This will block {{displayName}}", - "blocked_users": "Blocked users", - "unblock": "Unblock", - "no_friends_added": "You have no added friends", - "pending": "Pending", - "no_pending_invites": "You have no pending invites", - "no_blocked_users": "You have no blocked users", - "friend_code_copied": "Friend code copied", - "undo_friendship_modal_text": "This will undo your friendship with {{displayName}}", - "privacy_hint": "To adjust who can see this, go to the <0>Settings", - "locked_profile": "This profile is private", - "image_process_failure": "Failure while processing the image", - "required_field": "This field is required", - "displayname_min_length": "Display name must be at least 3 characters long", - "displayname_max_length": "Display name must be at most 50 characters long", - "report_profile": "Report this profile", - "report_reason": "Why are you reporting this profile?", - "report_description": "Additional information", - "report_description_placeholder": "Additional information", - "report": "Report", - "report_reason_hate": "Hate speech", - "report_reason_sexual_content": "Sexual content", - "report_reason_violence": "Violence", - "report_reason_spam": "Spam", - "report_reason_other": "Other", - "profile_reported": "Profile reported", - "your_friend_code": "Your friend code:", - "upload_banner": "Upload banner", - "uploading_banner": "Uploading banner…", - "background_image_updated": "Background image updated", - "stats": "Stats", - "achievements": "achievements", - "games": "Games", - "top_percentile": "Top {{percentile}}%", - "ranking_updated_weekly": "Ranking is updated weekly", - "playing": "Playing {{game}}", - "achievements_unlocked": "Achievements Unlocked", - "earned_points": "Earned points", - "show_achievements_on_profile": "Show your achievements on your profile", - "show_points_on_profile": "Show your earned points on your profile", - "error_adding_friend": "Could not send friend request. Please check friend code", - "friend_code_length_error": "Friend code must have 8 characters" - }, - "achievement": { - "achievement_unlocked": "Achievement unlocked", - "user_achievements": "{{displayName}}'s Achievements", - "your_achievements": "Your Achievements", - "unlocked_at": "Unlocked at: {{date}}", - "subscription_needed": "A Hydra Cloud subscription is required to see this content", - "new_achievements_unlocked": "Unlocked {{achievementCount}} new achievements from {{gameCount}} games", - "achievement_progress": "{{unlockedCount}}/{{totalCount}} achievements", - "achievements_unlocked_for_game": "Unlocked {{achievementCount}} new achievements for {{gameTitle}}", - "hidden_achievement_tooltip": "This is a hidden achievement", - "achievement_earn_points": "Earn {{points}} points with this achievement", - "earned_points": "Earned points:", - "available_points": "Available points:", - "how_to_earn_achievements_points": "How to earn achievements points?" - }, - "hydra_cloud": { - "subscription_tour_title": "Hydra Cloud Subscription", - "subscribe_now": "Subscribe now", - "cloud_saving": "Cloud saving", - "cloud_achievements": "Save your achievements on the cloud", - "animated_profile_picture": "Animated profile pictures", - "premium_support": "Premium Support", - "show_and_compare_achievements": "Show and compare your achievements to other users", - "animated_profile_banner": "Animated profile banner", - "hydra_cloud": "Hydra Cloud", - "hydra_cloud_feature_found": "You've just discovered a Hydra Cloud feature!", - "learn_more": "Learn More", - "debrid_description": "Download up to 4x faster with Nimbus" - } -} +{ + "language_name": "English", + "app": { + "successfully_signed_in": "Successfully signed in" + }, + "home": { + "featured": "Featured", + "surprise_me": "Surprise me", + "no_results": "No results found", + "start_typing": "Starting typing to search...", + "hot": "Hot now", + "weekly": "📅 Top games of the week", + "achievements": "🏆 Games to beat" + }, + "sidebar": { + "catalogue": "Catalogue", + "downloads": "Downloads", + "settings": "Settings", + "my_library": "My library", + "downloading_metadata": "{{title}} (Downloading metadata…)", + "paused": "{{title}} (Paused)", + "downloading": "{{title}} ({{percentage}} - Downloading…)", + "filter": "Filter library", + "home": "Home", + "queued": "{{title}} (Queued)", + "game_has_no_executable": "Game has no executable selected", + "sign_in": "Sign in", + "friends": "Friends", + "need_help": "Need help?", + "favorites": "Favorites", + "playable_button_title": "Show only games you can play now" + }, + "header": { + "search": "Search games", + "home": "Home", + "catalogue": "Catalogue", + "downloads": "Downloads", + "search_results": "Search results", + "settings": "Settings", + "version_available_install": "Version {{version}} available. Click here to restart and install.", + "version_available_download": "Version {{version}} available. Click here to download." + }, + "bottom_panel": { + "no_downloads_in_progress": "No downloads in progress", + "downloading_metadata": "Downloading {{title}} metadata…", + "downloading": "Downloading {{title}}… ({{percentage}} complete) - Completion {{eta}} - {{speed}}", + "calculating_eta": "Downloading {{title}}… ({{percentage}} complete) - Calculating remaining time…", + "checking_files": "Checking {{title}} files… ({{percentage}} complete)", + "installing_common_redist": "{{log}}…", + "installation_complete": "Installation complete", + "installation_complete_message": "Common redistributables installed successfully" + }, + "catalogue": { + "search": "Filter…", + "developers": "Developers", + "genres": "Genres", + "tags": "Tags", + "publishers": "Publishers", + "download_sources": "Download sources", + "result_count": "{{resultCount}} results", + "filter_count": "{{filterCount}} available", + "clear_filters": "Clear {{filterCount}} selected" + }, + "game_details": { + "open_download_options": "Open download options", + "download_options_zero": "No download option", + "download_options_one": "{{count}} download option", + "download_options_other": "{{count}} download options", + "updated_at": "Updated {{updated_at}}", + "install": "Install", + "resume": "Resume", + "pause": "Pause", + "cancel": "Cancel", + "remove": "Remove", + "space_left_on_disk": "{{space}} left on disk", + "eta": "Conclusion {{eta}}", + "calculating_eta": "Calculating remaining time…", + "downloading_metadata": "Downloading metadata…", + "filter": "Filter repacks", + "requirements": "System requirements", + "minimum": "Minimum", + "recommended": "Recommended", + "paused": "Paused", + "release_date": "Released on {{date}}", + "publisher": "Published by {{publisher}}", + "hours": "hours", + "minutes": "minutes", + "amount_hours": "{{amount}} hours", + "amount_minutes": "{{amount}} minutes", + "accuracy": "{{accuracy}}% accuracy", + "add_to_library": "Add to library", + "already_in_library": "Already in library", + "remove_from_library": "Remove from library", + "no_downloads": "No downloads available", + "play_time": "Played for {{amount}}", + "last_time_played": "Last played {{period}}", + "not_played_yet": "You haven't played {{title}} yet", + "next_suggestion": "Next suggestion", + "play": "Play", + "deleting": "Deleting installer…", + "close": "Close", + "playing_now": "Playing now", + "change": "Change", + "repacks_modal_description": "Choose the repack you want to download", + "select_folder_hint": "To change the default folder, go to the <0>Settings", + "download_now": "Download now", + "no_shop_details": "Could not retrieve shop details.", + "download_options": "Download options", + "download_path": "Download path", + "previous_screenshot": "Previous screenshot", + "next_screenshot": "Next screenshot", + "screenshot": "Screenshot {{number}}", + "open_screenshot": "Open screenshot {{number}}", + "download_settings": "Download settings", + "downloader": "Downloader", + "select_executable": "Select", + "no_executable_selected": "No executable selected", + "open_folder": "Open folder", + "open_download_location": "See downloaded files", + "create_shortcut": "Create desktop shortcut", + "clear": "Clear", + "remove_files": "Remove files", + "remove_from_library_title": "Are you sure?", + "remove_from_library_description": "This will remove {{game}} from your library", + "options": "Options", + "executable_section_title": "Executable", + "executable_section_description": "Path of the file that will be executed when \"Play\" is clicked", + "downloads_section_title": "Downloads", + "downloads_section_description": "Check out updates or other versions of this game", + "danger_zone_section_title": "Danger zone", + "danger_zone_section_description": "Remove this game from your library or the files downloaded by Hydra", + "download_in_progress": "Download in progress", + "download_paused": "Download paused", + "last_downloaded_option": "Last downloaded option", + "create_steam_shortcut": "Create Steam shortcut", + "create_shortcut_success": "Shortcut created successfully", + "you_might_need_to_restart_steam": "You might need to restart Steam to see the changes", + "create_shortcut_error": "Error creating shortcut", + "nsfw_content_title": "This game contains inappropriate content", + "nsfw_content_description": "{{title}} contains content that may not be suitable for all ages. Are you sure you want to continue?", + "allow_nsfw_content": "Continue", + "refuse_nsfw_content": "Go back", + "stats": "Stats", + "download_count": "Downloads", + "player_count": "Active players", + "download_error": "This download option is not available", + "download": "Download", + "executable_path_in_use": "Executable already in use by \"{{game}}\"", + "warning": "Warning:", + "hydra_needs_to_remain_open": "for this download, Hydra needs to remain open util it's completed. If Hydra closes before completing, you will lose your progress.", + "achievements": "Achievements", + "achievements_count": "Achievements {{unlockedCount}}/{{achievementsCount}}", + "cloud_save": "Cloud save", + "cloud_save_description": "Save your progress in the cloud and continue playing on any device", + "backups": "Backups", + "install_backup": "Install", + "delete_backup": "Delete", + "create_backup": "New backup", + "last_backup_date": "Last backup on {{date}}", + "no_backup_preview": "No save games were found for this title", + "restoring_backup": "Restoring backup ({{progress}} complete)…", + "uploading_backup": "Uploading backup…", + "no_backups": "You haven't created any backups for this game yet", + "backup_uploaded": "Backup uploaded", + "backup_deleted": "Backup deleted", + "backup_restored": "Backup restored", + "see_all_achievements": "See all achievements", + "sign_in_to_see_achievements": "Sign in to see achievements", + "mapping_method_automatic": "Automatic", + "mapping_method_manual": "Manual", + "mapping_method_label": "Mapping method", + "files_automatically_mapped": "Files automatically mapped", + "no_backups_created": "No backups created for this game", + "manage_files": "Manage files", + "loading_save_preview": "Searching for save games…", + "wine_prefix": "Wine Prefix", + "wine_prefix_description": "The Wine prefix used to run this game", + "launch_options": "Launch Options", + "launch_options_description": "Advanced users may choose to enter modifications to their launch options (experimental feature)", + "launch_options_placeholder": "No parameter specified", + "no_download_option_info": "No information available", + "backup_deletion_failed": "Failed to delete backup", + "max_number_of_artifacts_reached": "Maximum number of backups reached for this game", + "achievements_not_sync": "See how to synchronize your achievements", + "manage_files_description": "Manage which files will be backed up and restored", + "select_folder": "Select folder", + "backup_from": "Backup from {{date}}", + "automatic_backup_from": "Automatic backup from {{date}}", + "enable_automatic_cloud_sync": "Enable automatic cloud sync", + "custom_backup_location_set": "Custom backup location set", + "no_directory_selected": "No directory selected", + "no_write_permission": "Cannot download into this directory. Click here to learn more.", + "reset_achievements": "Reset achievements", + "reset_achievements_description": "This will reset all achievements for {{game}}", + "reset_achievements_title": "Are you sure?", + "reset_achievements_success": "Achievements successfully reset", + "reset_achievements_error": "Failed to reset achievements", + "download_error_gofile_quota_exceeded": "You have exceeded your Gofile monthly quota. Please await the quota to reset.", + "download_error_real_debrid_account_not_authorized": "Your Real-Debrid account is not authorized to make new downloads. Please check your account settings and try again.", + "download_error_not_cached_on_real_debrid": "This download is not available on Real-Debrid and polling download status from Real-Debrid is not yet available.", + "download_error_not_cached_on_torbox": "This download is not available on TorBox and polling download status from TorBox is not yet available.", + "download_error_not_cached_on_hydra": "This download is not available on Nimbus.", + "game_removed_from_favorites": "Game removed from favorites", + "game_added_to_favorites": "Game added to favorites", + "automatically_extract_downloaded_files": "Automatically extract downloaded files", + "create_start_menu_shortcut": "Create Start Menu shortcut", + "invalid_wine_prefix_path": "Invalid Wine prefix path", + "invalid_wine_prefix_path_description": "The path to the Wine prefix is invalid. Please check the path and try again.", + "missing_wine_prefix": "Wine prefix is required to create a backup on Linux", + "artifact_renamed": "Backup renamed successfully", + "rename_artifact": "Rename Backup", + "rename_artifact_description": "Rename the backup to a more descriptive name", + "artifact_name_label": "Backup name", + "artifact_name_placeholder": "Enter a name for the backup", + "save_changes": "Save changes", + "required_field": "This field is required", + "max_length_field": "This field must be less than {{length}} characters", + "freeze_backup": "Pin it so it's not overwritten by automatic backups", + "unfreeze_backup": "Unpin it", + "backup_frozen": "Backup pinned", + "backup_unfrozen": "Backup unpinned", + "backup_freeze_failed": "Failed to freeze backup", + "backup_freeze_failed_description": "You must leave at least one free slot for automatic backups", + "game_details": "Game Details", + "currency_symbol": "$", + "currency_country": "us", + "prices": "Prices", + "no_prices_found": "No prices found", + "retail_price": "Retail price", + "keyshop_price": "Keyshop price", + "historical_retail": "Historical retail", + "historical_keyshop": "Historical keyshop", + "supported_languages": "Supported languages", + "language": "Language", + "caption": "Caption", + "audio": "Audio" + }, + "activation": { + "title": "Activate Hydra", + "installation_id": "Installation ID:", + "enter_activation_code": "Enter your activation code", + "message": "If you don't know where to ask for this, then you shouldn't have this.", + "activate": "Activate", + "loading": "Loading…" + }, + "downloads": { + "resume": "Resume", + "pause": "Pause", + "eta": "Conclusion {{eta}}", + "paused": "Paused", + "verifying": "Verifying…", + "completed": "Completed", + "removed": "Not downloaded", + "cancel": "Cancel", + "filter": "Filter downloaded games", + "remove": "Remove", + "downloading_metadata": "Downloading metadata…", + "deleting": "Deleting installer…", + "delete": "Remove installer", + "delete_modal_title": "Are you sure?", + "delete_modal_description": "This will remove all the installation files from your computer", + "install": "Install", + "download_in_progress": "In progress", + "queued_downloads": "Queued downloads", + "downloads_completed": "Completed", + "queued": "Queued", + "no_downloads_title": "Such empty", + "no_downloads_description": "You haven't downloaded anything with Hydra yet, but it's never too late to start.", + "checking_files": "Checking files…", + "seeding": "Seeding", + "stop_seeding": "Stop seeding", + "resume_seeding": "Resume seeding", + "options": "Manage", + "extract": "Extract files", + "extracting": "Extracting files…" + }, + "settings": { + "downloads_path": "Downloads path", + "change": "Update", + "notifications": "Notifications", + "enable_download_notifications": "When a download is complete", + "gg_deals_api_key_description": "gg deals api key. Used to show the lowest price. (https://gg.deals/api/)", + "enable_repack_list_notifications": "When a new repack is added", + "real_debrid_api_token_label": "Real-Debrid API token", + "quit_app_instead_hiding": "Don't hide Hydra when closing", + "launch_with_system": "Launch Hydra on system start-up", + "general": "General", + "behavior": "Behavior", + "download_sources": "Download sources", + "language": "Language", + "api_token": "API Token", + "enable_real_debrid": "Enable Real-Debrid", + "real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to quickly download files, only limited by your internet speed.", + "debrid_invalid_token": "Invalid API token", + "debrid_api_token_hint": "You can get your API token <0>here", + "real_debrid_free_account_error": "The account \"{{username}}\" is a free account. Please subscribe to Real-Debrid", + "debrid_linked_message": "Account \"{{username}}\" linked", + "save_changes": "Save changes", + "changes_saved": "Changes successfully saved", + "download_sources_description": "Hydra will fetch the download links from these sources. The source URL must be a direct link to a .json file containing the download links.", + "validate_download_source": "Validate", + "remove_download_source": "Remove", + "add_download_source": "Add source", + "download_count_zero": "No download options", + "download_count_one": "{{countFormatted}} download option", + "download_count_other": "{{countFormatted}} download options", + "download_source_url": "Download source URL", + "add_download_source_description": "Insert the URL of the .json file", + "download_source_up_to_date": "Up-to-date", + "download_source_errored": "Errored", + "sync_download_sources": "Sync sources", + "removed_download_source": "Download source removed", + "removed_download_sources": "Download sources removed", + "cancel_button_confirmation_delete_all_sources": "No", + "confirm_button_confirmation_delete_all_sources": "Yes, delete everything", + "title_confirmation_delete_all_sources": "Delete all download sources", + "description_confirmation_delete_all_sources": "You will delete all download sources", + "button_delete_all_sources": "Remove all", + "added_download_source": "Added download source", + "download_sources_synced": "All download sources are synced", + "insert_valid_json_url": "Insert a valid JSON url", + "found_download_option_zero": "No download option found", + "found_download_option_one": "Found {{countFormatted}} download option", + "found_download_option_other": "Found {{countFormatted}} download options", + "import": "Import", + "public": "Public", + "private": "Private", + "friends_only": "Friends only", + "privacy": "Privacy", + "profile_visibility": "Profile visibility", + "profile_visibility_description": "Choose who can see your profile and library", + "required_field": "This field is required", + "source_already_exists": "This source has already been added", + "must_be_valid_url": "The source must be a valid URL", + "blocked_users": "Blocked users", + "user_unblocked": "User has been unblocked", + "enable_achievement_notifications": "When an achievement is unlocked", + "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", + "account": "Account", + "no_users_blocked": "You have no blocked users", + "subscription_active_until": "Your Hydra Cloud is active until {{date}}", + "manage_subscription": "Manage subscription", + "update_email": "Update email", + "update_password": "Update password", + "current_email": "Current email:", + "no_email_account": "You have not set an email yet", + "account_data_updated_successfully": "Account data updated successfully", + "renew_subscription": "Renew Hydra Cloud", + "subscription_expired_at": "Your subscription expired at {{date}}", + "no_subscription": "Enjoy Hydra in the best possible way", + "become_subscriber": "Be Hydra Cloud", + "subscription_renew_cancelled": "Automatic renewal is disabled", + "subscription_renews_on": "Your subscription renews on {{date}}", + "bill_sent_until": "Your next bill will be sent until this day", + "no_themes": "Seems like you don't have any themes yet, but no worries, click here to create your first masterpiece.", + "editor_tab_code": "Code", + "editor_tab_info": "Info", + "editor_tab_save": "Save", + "web_store": "Web store", + "clear_themes": "Clear", + "create_theme": "Create", + "create_theme_modal_title": "Create custom theme", + "create_theme_modal_description": "Create a new theme to customize Hydra's appearance", + "theme_name": "Name", + "insert_theme_name": "Insert theme name", + "set_theme": "Set theme", + "unset_theme": "Unset theme", + "delete_theme": "Delete theme", + "edit_theme": "Edit theme", + "delete_all_themes": "Delete all themes", + "delete_all_themes_description": "This will delete all your custom themes", + "delete_theme_description": "This will delete the theme {{theme}}", + "cancel": "Cancel", + "appearance": "Appearance", + "enable_torbox": "Enable TorBox", + "torbox_description": "TorBox is your premium seedbox service rivaling even the best servers on the market.", + "torbox_account_linked": "TorBox account linked", + "create_real_debrid_account": "Click here if you don't have a Real-Debrid account yet", + "create_torbox_account": "Click here if you don't have a TorBox account yet", + "real_debrid_account_linked": "Real-Debrid account linked", + "name_min_length": "Theme name must be at least 3 characters long", + "import_theme": "Import theme", + "import_theme_description": "You will import {{theme}} from the theme store", + "error_importing_theme": "Error importing theme", + "theme_imported": "Theme imported successfully", + "enable_friend_request_notifications": "When a friend request is received", + "enable_auto_install": "Download updates automatically", + "common_redist": "Common redistributables", + "common_redist_description": "Common redistributables are required to run some games. Installing them is recommended to avoid issues.", + "install_common_redist": "Install", + "installing_common_redist": "Installing…", + "show_download_speed_in_megabytes": "Show download speed in megabytes per second", + "extract_files_by_default": "Extract files by default after download", + "enable_steam_achievements": "Enable search for Steam achievements", + "achievement_custom_notification_position": "Achievement custom notification position", + "top-left": "Top left", + "top-center": "Top center", + "top-right": "Top right", + "bottom-left": "Bottom left", + "bottom-center": "Bottom center", + "bottom-right": "Bottom right", + "enable_achievement_custom_notifications": "Enable achievement custom notifications", + "alignment": "Alignment", + "variation": "Variation", + "default": "Default", + "rare": "Rare", + "platinum": "Platinum", + "hidden": "Hidden", + "test_notification": "Test notification", + "notification_preview": "Achievement Notification Preview", + "enable_friend_start_game_notifications": "When a friend starts playing a game" + }, + "notifications": { + "download_complete": "Download complete", + "game_ready_to_install": "{{title}} is ready to install", + "repack_list_updated": "Repack list updated", + "repack_count_one": "{{count}} repack added", + "repack_count_other": "{{count}} repacks added", + "new_update_available": "Version {{version}} available", + "restart_to_install_update": "Restart Hydra to install the update", + "notification_achievement_unlocked_title": "Achievement unlocked for {{game}}", + "notification_achievement_unlocked_body": "{{achievement}} and other {{count}} were unlocked", + "new_friend_request_description": "{{displayName}} sent you a friend request", + "new_friend_request_title": "New friend request", + "extraction_complete": "Extraction complete", + "game_extracted": "{{title}} extracted successfully", + "friend_started_playing_game": "{{displayName}} started playing a game", + "test_achievement_notification_title": "This is a test notification", + "test_achievement_notification_description": "Pretty cool, huh?" + }, + "system_tray": { + "open": "Open Hydra", + "quit": "Quit" + }, + "game_card": { + "available_one": "Available", + "available_other": "Available", + "no_downloads": "No downloads available" + }, + "binary_not_found_modal": { + "title": "Programs not installed", + "description": "Wine or Lutris executables were not found on your system", + "instructions": "Check the correct way to install any of them on your Linux distro so that the game can run normally" + }, + "modal": { + "close": "Close button" + }, + "forms": { + "toggle_password_visibility": "Toggle password visibility" + }, + "user_profile": { + "amount_hours": "{{amount}} hours", + "amount_minutes": "{{amount}} minutes", + "last_time_played": "Last played {{period}}", + "activity": "Recent Activity", + "library": "Library", + "total_play_time": "Total playtime", + "no_recent_activity_title": "Hmmm… nothing here", + "no_recent_activity_description": "You haven't played any games recently. It's time to change that!", + "display_name": "Display name", + "saving": "Saving", + "save": "Save", + "edit_profile": "Edit Profile", + "saved_successfully": "Saved successfully", + "try_again": "Please, try again", + "sign_out_modal_title": "Are you sure?", + "cancel": "Cancel", + "successfully_signed_out": "Successfully signed out", + "sign_out": "Sign out", + "playing_for": "Playing for {{amount}}", + "sign_out_modal_text": "Your library is linked with your current account. When signing out, your library will not be visible anymore, and any progress will not be saved. Continue with sign out?", + "add_friends": "Add Friends", + "add": "Add", + "friend_code": "Friend code", + "see_profile": "See profile", + "sending": "Sending", + "friend_request_sent": "Friend request sent", + "friends": "Friends", + "friends_list": "Friends list", + "user_not_found": "User not found", + "block_user": "Block user", + "add_friend": "Add friend", + "request_sent": "Request sent", + "request_received": "Request received", + "accept_request": "Accept request", + "ignore_request": "Ignore request", + "cancel_request": "Cancel request", + "undo_friendship": "Undo friendship", + "request_accepted": "Request accepted", + "user_blocked_successfully": "User blocked successfully", + "user_block_modal_text": "This will block {{displayName}}", + "blocked_users": "Blocked users", + "unblock": "Unblock", + "no_friends_added": "You have no added friends", + "pending": "Pending", + "no_pending_invites": "You have no pending invites", + "no_blocked_users": "You have no blocked users", + "friend_code_copied": "Friend code copied", + "undo_friendship_modal_text": "This will undo your friendship with {{displayName}}", + "privacy_hint": "To adjust who can see this, go to the <0>Settings", + "locked_profile": "This profile is private", + "image_process_failure": "Failure while processing the image", + "required_field": "This field is required", + "displayname_min_length": "Display name must be at least 3 characters long", + "displayname_max_length": "Display name must be at most 50 characters long", + "report_profile": "Report this profile", + "report_reason": "Why are you reporting this profile?", + "report_description": "Additional information", + "report_description_placeholder": "Additional information", + "report": "Report", + "report_reason_hate": "Hate speech", + "report_reason_sexual_content": "Sexual content", + "report_reason_violence": "Violence", + "report_reason_spam": "Spam", + "report_reason_other": "Other", + "profile_reported": "Profile reported", + "your_friend_code": "Your friend code:", + "upload_banner": "Upload banner", + "uploading_banner": "Uploading banner…", + "background_image_updated": "Background image updated", + "stats": "Stats", + "achievements": "achievements", + "games": "Games", + "top_percentile": "Top {{percentile}}%", + "ranking_updated_weekly": "Ranking is updated weekly", + "playing": "Playing {{game}}", + "achievements_unlocked": "Achievements Unlocked", + "earned_points": "Earned points", + "show_achievements_on_profile": "Show your achievements on your profile", + "show_points_on_profile": "Show your earned points on your profile", + "error_adding_friend": "Could not send friend request. Please check friend code", + "friend_code_length_error": "Friend code must have 8 characters" + }, + "achievement": { + "achievement_unlocked": "Achievement unlocked", + "user_achievements": "{{displayName}}'s Achievements", + "your_achievements": "Your Achievements", + "unlocked_at": "Unlocked at: {{date}}", + "subscription_needed": "A Hydra Cloud subscription is required to see this content", + "new_achievements_unlocked": "Unlocked {{achievementCount}} new achievements from {{gameCount}} games", + "achievement_progress": "{{unlockedCount}}/{{totalCount}} achievements", + "achievements_unlocked_for_game": "Unlocked {{achievementCount}} new achievements for {{gameTitle}}", + "hidden_achievement_tooltip": "This is a hidden achievement", + "achievement_earn_points": "Earn {{points}} points with this achievement", + "earned_points": "Earned points:", + "available_points": "Available points:", + "how_to_earn_achievements_points": "How to earn achievements points?" + }, + "hydra_cloud": { + "subscription_tour_title": "Hydra Cloud Subscription", + "subscribe_now": "Subscribe now", + "cloud_saving": "Cloud saving", + "cloud_achievements": "Save your achievements on the cloud", + "animated_profile_picture": "Animated profile pictures", + "premium_support": "Premium Support", + "show_and_compare_achievements": "Show and compare your achievements to other users", + "animated_profile_banner": "Animated profile banner", + "hydra_cloud": "Hydra Cloud", + "hydra_cloud_feature_found": "You've just discovered a Hydra Cloud feature!", + "learn_more": "Learn More", + "debrid_description": "Download up to 4x faster with Nimbus" + } +} diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index a4bcf826..55729de1 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -1,559 +1,559 @@ -{ - "language_name": "Português (Brasil)", - "app": { - "successfully_signed_in": "Autenticado com sucesso" - }, - "home": { - "featured": "Destaques", - "hot": "Populares", - "weekly": "📅 Mais baixados da semana", - "achievements": "🏆 Pra platinar", - "surprise_me": "Surpreenda-me", - "no_results": "Nenhum resultado encontrado", - "start_typing": "Comece a digitar para pesquisar…" - }, - "sidebar": { - "catalogue": "Catálogo", - "downloads": "Downloads", - "settings": "Ajustes", - "my_library": "Biblioteca", - "downloading_metadata": "{{title}} (Baixando metadados…)", - "paused": "{{title}} (Pausado)", - "downloading": "{{title}} ({{percentage}} - Baixando…)", - "filter": "Buscar", - "home": "Início", - "queued": "{{title}} (Na fila)", - "game_has_no_executable": "Jogo não possui executável selecionado", - "sign_in": "Login", - "friends": "Amigos", - "need_help": "Precisa de ajuda?", - "favorites": "Favoritos" - }, - "header": { - "search": "Buscar jogos", - "catalogue": "Catálogo", - "downloads": "Downloads", - "search_results": "Resultados da busca", - "settings": "Ajustes", - "home": "Início", - "version_available_install": "Versão {{version}} disponível. Clique aqui para reiniciar e instalar.", - "version_available_download": "Versão {{version}} disponível. Clique aqui para fazer o download." - }, - "bottom_panel": { - "no_downloads_in_progress": "Sem downloads em andamento", - "downloading_metadata": "Baixando metadados de {{title}}…", - "downloading": "Baixando {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}", - "calculating_eta": "Baixando {{title}}… ({{percentage}} concluído) - Calculando tempo restante…", - "checking_files": "Verificando arquivos de {{title}}…", - "installing_common_redist": "{{log}}…", - "installation_complete": "Instalação concluída", - "installation_complete_message": "Componentes recomendados instalados com sucesso" - }, - "game_details": { - "open_download_options": "Ver opções de download", - "download_options_zero": "Sem opções de download", - "download_options_one": "{{count}} opção de download", - "download_options_other": "{{count}} opções de download", - "updated_at": "Atualizado {{updated_at}}", - "resume": "Retomar", - "pause": "Pausar", - "cancel": "Cancelar", - "remove": "Remover", - "space_left_on_disk": "{{space}} livres em disco", - "eta": "Conclusão {{eta}}", - "calculating_eta": "Calculando tempo restante…", - "downloading_metadata": "Baixando metadados…", - "filter": "Filtrar repacks", - "requirements": "Requisitos de sistema", - "minimum": "Mínimos", - "recommended": "Recomendados", - "paused": "Pausado", - "release_date": "Lançado em {{date}}", - "publisher": "Publicado por {{publisher}}", - "hours": "horas", - "minutes": "minutos", - "amount_hours": "{{amount}} horas", - "amount_minutes": "{{amount}} minutos", - "accuracy": "{{accuracy}}% de precisão", - "add_to_library": "Adicionar à biblioteca", - "already_in_library": "Já está na biblioteca", - "remove_from_library": "Remover da biblioteca", - "no_downloads": "Nenhum download disponível", - "play_time": "Jogou por {{amount}}", - "next_suggestion": "Próxima sugestão", - "install": "Instalar", - "last_time_played": "Última sessão {{period}}", - "play": "Jogar", - "not_played_yet": "Você ainda não jogou {{title}}", - "close": "Fechar", - "deleting": "Excluindo instalador…", - "playing_now": "Jogando agora", - "change": "Explorar", - "repacks_modal_description": "Escolha o repack do jogo que deseja baixar", - "select_folder_hint": "Para trocar o diretório padrão, acesse a <0>Tela de Ajustes", - "download_now": "Iniciar download", - "no_shop_details": "Não foi possível obter os detalhes da loja.", - "download_options": "Opções de download", - "download_path": "Diretório de download", - "previous_screenshot": "Captura de tela anterior", - "next_screenshot": "Próxima captura de tela", - "screenshot": "Captura de tela {{number}}", - "open_screenshot": "Ver captura de tela {{number}}", - "download_settings": "Ajustes do download", - "downloader": "Downloader", - "select_executable": "Explorar", - "no_executable_selected": "Nenhum executável selecionado", - "open_folder": "Abrir pasta", - "open_download_location": "Ver arquivos baixados", - "create_shortcut": "Criar atalho na área de trabalho", - "remove_files": "Remover arquivos", - "options": "Gerenciar", - "remove_from_library_description": "Isso irá remover {{game}} da sua biblioteca", - "remove_from_library_title": "Tem certeza?", - "executable_section_title": "Executável", - "executable_section_description": "O caminho do arquivo que será executado ao clicar em \"Jogar\"", - "downloads_section_title": "Downloads", - "downloads_section_description": "Confira atualizações ou versões diferentes para este mesmo título", - "danger_zone_section_title": "Zona de perigo", - "danger_zone_section_description": "Remova o jogo da sua biblioteca ou os arquivos que foram baixados pelo Hydra", - "download_in_progress": "Download em andamento", - "download_paused": "Download pausado", - "last_downloaded_option": "Última opção baixada", - "create_steam_shortcut": "Criar atalho na Steam", - "create_shortcut_success": "Atalho criado com sucesso", - "you_might_need_to_restart_steam": "Você pode precisar reiniciar a Steam para ver as alterações", - "create_shortcut_error": "Erro ao criar atalho", - "nsfw_content_title": "Este jogo contém conteúdo inapropriado", - "nsfw_content_description": "{{title}} contém conteúdo que pode não ser apropriado para todas as idades. Você deseja continuar?", - "allow_nsfw_content": "Continuar", - "refuse_nsfw_content": "Voltar", - "stats": "Estatísticas", - "download_count": "Downloads", - "player_count": "Jogadores ativos", - "download_error": "Essa opção de download falhou", - "download": "Baixar", - "executable_path_in_use": "Executável em uso por \"{{game}}\"", - "warning": "Aviso:", - "hydra_needs_to_remain_open": "para este download, o Hydra precisa ficar aberto até a conclusão. Caso o Hydra encerre antes da conclusão, perderá seu progresso.", - "achievements": "Conquistas", - "achievements_count": "Conquistas ({{unlockedCount}}/{{achievementsCount}})", - "cloud_save": "Salvamento em nuvem", - "cloud_save_description": "Mantenha seu progresso na nuvem e continue de onde parou em qualquer dispositivo", - "backups": "Backups", - "install_backup": "Restaurar", - "delete_backup": "Apagar", - "create_backup": "Novo backup", - "last_backup_date": "Último backup em {{date}}", - "no_backup_preview": "Não foi possível encontrar nenhum salvamento para este jogo", - "restoring_backup": "Restaurando backup ({{progress}} concluído)…", - "uploading_backup": "Criando backup…", - "no_backups": "Você ainda não fez nenhum backup deste jogo", - "backup_uploaded": "Backup criado", - "backup_deleted": "Backup apagado", - "backup_restored": "Backup restaurado", - "see_all_achievements": "Ver todas as conquistas", - "sign_in_to_see_achievements": "Faça login para ver as conquistas", - "mapping_method_automatic": "Automático", - "mapping_method_manual": "Manual", - "mapping_method_label": "Método de mapeamento", - "files_automatically_mapped": "Arquivos automaticamente mapeados", - "no_backups_created": "Nenhum backup criado para este jogo", - "manage_files": "Gerenciar arquivos", - "loading_save_preview": "Buscando por arquivos de salvamento…", - "wine_prefix": "Prefixo Wine", - "wine_prefix_description": "O prefixo Wine que foi utilizado para instalar o jogo", - "launch_options": "Opções de Inicialização", - "launch_options_description": "Usuários avançados podem adicionar opções de inicialização no jogo (experimental)", - "launch_options_placeholder": "Nenhum parâmetro informado", - "no_download_option_info": "Sem informações disponíveis", - "backup_deletion_failed": "Falha ao apagar backup", - "max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo", - "achievements_not_sync": "Veja como exibir suas conquistas no perfil", - "backup_from": "Backup de {{date}}", - "automatic_backup_from": "Backup automático de {{date}}", - "enable_automatic_cloud_sync": "Habilitar sincronização automática na nuvem", - "custom_backup_location_set": "Localização customizada selecionada", - "select_folder": "Selecione a pasta", - "manage_files_description": "Gerencie quais arquivos serão feitos backup", - "clear": "Limpar", - "no_directory_selected": "Nenhum diretório selecionado", - "reset_achievements": "Resetar conquistas", - "reset_achievements_description": "Isso irá resetar todas as conquistas de {{game}}", - "reset_achievements_title": "Tem certeza?", - "reset_achievements_success": "Conquistas resetadas com sucesso", - "reset_achievements_error": "Falha ao resetar conquistas", - "no_write_permission": "Não é possível baixar nesse diretório. Clique aqui para saber mais.", - "download_error_gofile_quota_exceeded": "Você excedeu sua cota mensal do Gofile. Por favor, aguarde a cota resetar.", - "download_error_real_debrid_account_not_authorized": "Sua conta do Real-Debrid não está autorizada a fazer novos downloads. Por favor, verifique sua assinatura e tente novamente.", - "download_error_not_cached_on_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.", - "download_error_not_cached_on_torbox": "Este download não está disponível no TorBox e a verificação do status do download não está disponível.", - "download_error_not_cached_on_hydra": "Este download não está disponível no Nimbus.", - "game_removed_from_favorites": "Jogo removido dos favoritos", - "game_added_to_favorites": "Jogo adicionado aos favoritos", - "automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados", - "create_start_menu_shortcut": "Criar atalho no Menu Iniciar", - "invalid_wine_prefix_path": "Caminho do prefixo Wine inválido", - "invalid_wine_prefix_path_description": "O caminho para o prefixo Wine é inválido. Por favor, verifique o caminho e tente novamente.", - "artifact_renamed": "Backup renomeado com sucesso", - "rename_artifact": "Renomear Backup", - "rename_artifact_description": "Renomeie o backup para um nome mais descritivo", - "artifact_name_label": "Nome do backup", - "artifact_name_placeholder": "Insira um nome para o backup", - "save_changes": "Salvar mudanças", - "required_field": "Este campo é obrigatório", - "max_length_field": "Este campo deve ter menos de {{length}} caracteres", - "freeze_backup": "Fixar para não ser apagado por backups automáticos", - "unfreeze_backup": "Remover dos fixados", - "backup_frozen": "Backup fixado", - "backup_unfrozen": "Backup removido dos fixados", - "backup_freeze_failed": "Falha ao fixar backup", - "backup_freeze_failed_description": "Você deve deixar pelo menos um espaço livre para backups automáticos", - "game_details": "Detalhes do Jogo", - "currency_symbol": "R$", - "currency_country": "br", - "prices": "Preços", - "no_prices_found": "Nenhum preço encontrado", - "retail_price": "Preço de lojas oficiais", - "keyshop_price": "Preço em keyshops", - "historical_retail": "Preço histórico de lojas oficiais", - "historical_keyshop": "Preço histórico em keyshops", - "supported_languages": "Idiomas suportados", - "language": "Idioma", - "caption": "Legenda", - "audio": "Áudio" - }, - "activation": { - "title": "Ativação", - "installation_id": "ID da instalação:", - "enter_activation_code": "Insira seu código de ativação", - "message": "Se você não sabe onde conseguir o código, talvez você não devesse estar aqui.", - "activate": "Ativar", - "loading": "Carregando…" - }, - "downloads": { - "resume": "Retomar", - "pause": "Pausar", - "eta": "Conclusão {{eta}}", - "paused": "Pausado", - "verifying": "Verificando…", - "completed": "Concluído", - "removed": "Cancelado", - "cancel": "Cancelar", - "filter": "Filtrar jogos baixados", - "remove": "Remover", - "downloading_metadata": "Baixando metadados…", - "delete": "Remover instalador", - "delete_modal_description": "Isso removerá todos os arquivos de instalação do seu computador", - "delete_modal_title": "Tem certeza?", - "deleting": "Excluindo instalador…", - "install": "Instalar", - "download_in_progress": "Baixando agora", - "queued_downloads": "Na fila", - "downloads_completed": "Concluído", - "queued": "Na fila", - "no_downloads_title": "Nada por aqui…", - "no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.", - "checking_files": "Verificando arquivos…", - "seeding": "Semeando", - "stop_seeding": "Parar de semear", - "resume_seeding": "Semear", - "options": "Gerenciar", - "extract": "Extrair arquivos", - "extracting": "Extraindo arquivos…" - }, - "settings": { - "downloads_path": "Diretório dos downloads", - "change": "Explorar...", - "notifications": "Notificações", - "enable_download_notifications": "Quando um download for concluído", - "gg_deals_api_key_description": "gg deals api key. Usado para mostrar o menor preço. (https://gg.deals/api/)", - "enable_repack_list_notifications": "Quando a lista de repacks for atualizada", - "real_debrid_api_token_label": "Token de API do Real-Debrid", - "quit_app_instead_hiding": "Encerrar o Hydra em vez de apenas minimizá-lo ao fechar", - "launch_with_system": "Iniciar o Hydra junto com o sistema", - "general": "Geral", - "behavior": "Comportamento", - "download_sources": "Fontes de download", - "language": "Idioma", - "api_token": "Token de API", - "enable_real_debrid": "Habilitar Real-Debrid", - "debrid_api_token_hint": "Você pode obter seu token de API <0>aqui", - "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos instantaneamente e com a melhor velocidade da sua Internet.", - "debrid_invalid_token": "Token de API inválido", - "real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, assine a Real-Debrid", - "debrid_linked_message": "Conta \"{{username}}\" vinculada", - "save_changes": "Salvar mudanças", - "changes_saved": "Ajustes salvos com sucesso", - "download_sources_description": "Hydra vai buscar links de download em todas as fontes habilitadas. A URL da fonte deve ser um link direto para um arquivo .json contendo uma lista de links.", - "validate_download_source": "Validar", - "remove_download_source": "Remover", - "add_download_source": "Adicionar fonte", - "download_count_zero": "Sem downloads na lista", - "download_count_one": "{{countFormatted}} download na lista", - "download_count_other": "{{countFormatted}} downloads na lista", - "download_source_url": "URL da fonte", - "add_download_source_description": "Insira a URL contendo o arquivo .json", - "download_source_up_to_date": "Sincronizada", - "download_source_errored": "Falhou", - "sync_download_sources": "Sincronizar", - "removed_download_source": "Fonte removida", - "removed_download_sources": "Fontes removidas", - "cancel_button_confirmation_delete_all_sources": "Não", - "confirm_button_confirmation_delete_all_sources": "Sim, excluir tudo", - "title_confirmation_delete_all_sources": "Remover todas as fontes de download", - "description_confirmation_delete_all_sources": "Você irá remover todas as fontes de download. Deseja prosseguir?", - "button_delete_all_sources": "Remover todas", - "added_download_source": "Fonte adicionada", - "download_sources_synced": "As fontes foram sincronizadas", - "insert_valid_json_url": "Insira a url de um JSON válido", - "found_download_option_zero": "Nenhuma opção de download encontrada", - "found_download_option_one": "{{countFormatted}} opção de download encontrada", - "found_download_option_other": "{{countFormatted}} opções de download encontradas", - "import": "Importar", - "privacy": "Privacidade", - "private": "Privado", - "friends_only": "Apenas amigos", - "public": "Público", - "profile_visibility": "Visibilidade do perfil", - "profile_visibility_description": "Escolha quem pode ver seu perfil e biblioteca", - "required_field": "Este campo é obrigatório", - "source_already_exists": "Essa fonte já foi adicionada", - "must_be_valid_url": "A fonte deve ser uma URL válida", - "blocked_users": "Usuários bloqueados", - "user_unblocked": "Usuário desbloqueado", - "enable_achievement_notifications": "Quando uma conquista é desbloqueada", - "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", - "account": "Conta", - "no_users_blocked": "Você não bloqueou nenhum usuário", - "subscription_active_until": "Sua assinatura Hydra Cloud ficará ativa até {{date}}", - "manage_subscription": "Gerenciar assinatura", - "update_email": "Atualizar email", - "update_password": "Atualizar senha", - "current_email": "Email atual:", - "no_email_account": "Você ainda não adicionou um email a sua conta", - "account_data_updated_successfully": "Dados da conta atualizados com sucesso", - "renew_subscription": "Renovar Hydra Cloud", - "subscription_expired_at": "Sua assinatura expirou em {{date}}", - "no_subscription": "Aproveite o Hydra da melhor forma possível", - "become_subscriber": "Seja Hydra Cloud", - "subscription_renew_cancelled": "A renovação automática está desativada", - "subscription_renews_on": "Sua assinatura renova dia {{date}}", - "bill_sent_until": "Sua próxima cobrança será enviada até esse dia", - "no_themes": "Parece que você ainda não tem nenhum tema. Não se preocupe, clique aqui para criar sua primeira obra de arte.", - "editor_tab_save": "Salvar", - "web_store": "Loja de temas", - "clear_themes": "Limpar", - "create_theme": "Criar", - "create_theme_modal_title": "Criar tema customizado", - "create_theme_modal_description": "Criar novo tema para customizar a aparência do Hydra", - "theme_name": "Nome", - "insert_theme_name": "Insira o nome do tema", - "set_theme": "Habilitar tema", - "unset_theme": "Desabilitar tema", - "delete_theme": "Deletar tema", - "edit_theme": "Editar tema", - "delete_all_themes": "Deletar todos os temas", - "delete_all_themes_description": "Isso irá deletar todos os seus temas", - "delete_theme_description": "Isso irá deletar o tema {{theme}}", - "cancel": "Cancelar", - "appearance": "Aparência", - "enable_torbox": "Habilitar TorBox", - "torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.", - "torbox_account_linked": "Conta do TorBox vinculada", - "create_real_debrid_account": "Clique aqui se você ainda não tem uma conta do Real-Debrid", - "create_torbox_account": "Clique aqui se você ainda não tem uma conta do TorBox", - "real_debrid_account_linked": "Conta Real-Debrid associada", - "name_min_length": "O nome do tema deve ter pelo menos 3 caracteres", - "import_theme": "Importar tema", - "import_theme_description": "Você irá importar {{theme}} da loja de temas", - "error_importing_theme": "Erro ao importar tema", - "theme_imported": "Tema importado com sucesso", - "enable_friend_request_notifications": "Quando um pedido de amizade é recebido", - "enable_auto_install": "Baixar atualizações automaticamente", - "common_redist": "Componentes recomendados", - "common_redist_description": "Componentes recomendados são necessários para executar alguns jogos. A instalação deles é recomendada para evitar problemas.", - "install_common_redist": "Instalar", - "installing_common_redist": "Instalando…", - "show_download_speed_in_megabytes": "Exibir taxas de download em megabytes por segundo", - "extract_files_by_default": "Extrair arquivos automaticamente após o download", - "enable_steam_achievements": "Habilitar busca por conquistas da Steam", - "enable_achievement_custom_notifications": "Habilitar notificações customizadas de conquistas", - "top-left": "Superior esquerdo", - "top-center": "Superior central", - "top-right": "Superior direito", - "bottom-left": "Inferior esquerdo", - "bottom-right": "Inferior direito", - "bottom-center": "Inferior central", - "achievement_custom_notification_position": "Posição das notificações customizadas de conquista", - "alignment": "Alinhamento", - "variation": "Variação", - "default": "Padrão", - "rare": "Rara", - "platinum": "Platina", - "hidden": "Oculta", - "test_notification": "Testar notificação", - "notification_preview": "Prévia da Notificação de Conquistas", - "enable_friend_start_game_notifications": "Quando um amigo iniciar um jogo" - }, - "notifications": { - "download_complete": "Download concluído", - "game_ready_to_install": "{{title}} está pronto para ser instalado", - "repack_list_updated": "Lista de repacks atualizada", - "repack_count_one": "{{count}} novo repack", - "repack_count_other": "{{count}} novos repacks", - "new_update_available": "Versão {{version}} disponível", - "restart_to_install_update": "Reinicie o Hydra para instalar a nova versão", - "new_friend_request_title": "Novo pedido de amizade", - "new_friend_request_description": "{{displayName}} te enviou um pedido de amizade", - "extraction_complete": "Extração concluída", - "game_extracted": "{{title}} extraído com sucesso", - "friend_started_playing_game": "{{displayName}} começou a jogar", - "test_achievement_notification_title": "Esta é uma notificação de teste", - "test_achievement_notification_description": "Bem legal, né?" - }, - "system_tray": { - "open": "Abrir Hydra", - "quit": "Fechar" - }, - "game_card": { - "available_one": "Disponível", - "available_other": "Disponíveis", - "no_downloads": "Sem downloads disponíveis" - }, - "binary_not_found_modal": { - "title": "Programas não instalados", - "description": "Os executáveis do Wine ou Lutris não foram encontrados em seu sistema.", - "instructions": "Verifique a forma correta de instalar algum deles no seu distro Linux, garantindo assim a execução normal do jogo" - }, - "catalogue": { - "search": "Filtrar…", - "developers": "Desenvolvedores", - "genres": "Gêneros", - "tags": "Marcadores", - "publishers": "Distribuidoras", - "download_sources": "Fontes de download", - "result_count": "{{resultCount}} resultados", - "filter_count": "{{filterCount}} disponíveis", - "clear_filters": "Limpar {{filterCount}} selecionados" - }, - "modal": { - "close": "Botão de fechar" - }, - "forms": { - "toggle_password_visibility": "Alternar visibilidade da senha" - }, - "user_profile": { - "amount_hours": "{{amount}} horas", - "amount_minutes": "{{amount}} minutos", - "last_time_played": "Última sessão {{period}}", - "activity": "Atividades recentes", - "library": "Biblioteca", - "total_play_time": "Tempo total de jogo", - "no_recent_activity_title": "Hmmm… nada por aqui", - "no_recent_activity_description": "Parece que você não jogou nada recentemente. Que tal começar agora?", - "display_name": "Nome de exibição", - "saving": "Salvando…", - "save": "Salvar", - "edit_profile": "Editar perfil", - "saved_successfully": "Salvo com sucesso", - "try_again": "Por favor, tente novamente", - "cancel": "Cancelar", - "successfully_signed_out": "Deslogado com sucesso", - "sign_out": "Sair da conta", - "sign_out_modal_title": "Deseja mesmo sair?", - "playing_for": "Jogando por {{amount}}", - "sign_out_modal_text": "Sua biblioteca de jogos está associada com a sua conta atual. Ao sair, sua biblioteca não aparecerá mais no Hydra e qualquer progresso não será salvo. Deseja continuar?", - "add_friends": "Adicionar Amigos", - "friend_code": "Código de amigo", - "see_profile": "Ver perfil", - "friend_request_sent": "Pedido de amizade enviado", - "friends": "Amigos", - "add": "Adicionar", - "sending": "Enviando", - "friends_list": "Lista de amigos", - "user_not_found": "Usuário não encontrado", - "block_user": "Bloquear", - "add_friend": "Adicionar amigo", - "request_sent": "Pedido enviado", - "request_received": "Pedido recebido", - "accept_request": "Aceitar pedido", - "ignore_request": "Ignorar pedido", - "cancel_request": "Cancelar pedido", - "undo_friendship": "Desfazer amizade", - "request_accepted": "Pedido de amizade aceito", - "user_blocked_successfully": "Usuário bloqueado com sucesso", - "user_block_modal_text": "Bloquear {{displayName}}", - "blocked_users": "Usuários bloqueados", - "unblock": "Desbloquear", - "no_friends_added": "Você ainda não possui amigos adicionados", - "pending": "Pendentes", - "no_pending_invites": "Você não possui convites de amizade pendentes", - "no_blocked_users": "Você não tem nenhum usuário bloqueado", - "friend_code_copied": "Código de amigo copiado", - "undo_friendship_modal_text": "Isso irá remover sua amizade com {{displayName}}", - "privacy_hint": "Pra controlar quem pode ver seu perfil, acesse a <0>Tela de Configurações", - "image_process_failure": "Falha ao processar a imagem", - "required_field": "Este campo é obrigatório", - "displayname_min_length": "Nome de exibição deve ter pelo menos 3 caracteres", - "displayname_max_length": "Nome de exibição deve ter no máximo 50 caracteres", - "locked_profile": "Este perfil é privado", - "report_profile": "Reportar este perfil", - "report_reason": "Por que você deseja reportar este perfil?", - "report_description": "Informações adicionais", - "report_description_placeholder": "Insira aqui", - "report": "Reportar", - "report_reason_hate": "Discurso de ódio", - "report_reason_sexual_content": "Conteúdo sexual", - "report_reason_violence": "Violência", - "report_reason_spam": "Spam", - "report_reason_other": "Outro", - "profile_reported": "Perfil reportado", - "your_friend_code": "Seu código de amigo:", - "upload_banner": "Carregar banner", - "uploading_banner": "Carregando banner…", - "background_image_updated": "Imagem de fundo salva", - "stats": "Estatísticas", - "achievements": "conquistas", - "games": "Jogos", - "ranking_updated_weekly": "O ranking é atualizado semanalmente", - "playing": "Jogando {{game}}", - "achievements_unlocked": "Conquistas desbloqueadas", - "earned_points": "Pontos ganhos", - "show_achievements_on_profile": "Exiba suas conquistas no perfil", - "show_points_on_profile": "Exiba seus pontos ganhos no perfil", - "error_adding_friend": "Não foi possível enviar o pedido de amizade. Verifique o código de amizade inserido", - "friend_code_length_error": "Código de amigo deve ter 8 caracteres" - }, - "achievement": { - "achievement_unlocked": "Conquista desbloqueada", - "your_achievements": "Suas Conquistas", - "user_achievements": "Conquistas de {{displayName}}", - "unlocked_at": "Desbloqueada em: {{date}}", - "subscription_needed": "Você precisa de uma assinatura Hydra Cloud para visualizar este conteúdo", - "new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos", - "achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas", - "achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}", - "hidden_achievement_tooltip": "Esta é uma conquista oculta", - "achievement_earn_points": "Ganhe {{points}} pontos com essa conquista", - "earned_points": "Pontos ganhos:", - "available_points": "Pontos disponíveis:", - "how_to_earn_achievements_points": "Como desbloquear pontos nas conquistas?" - }, - "hydra_cloud": { - "subscription_tour_title": "Assinatura Hydra Cloud", - "hydra_cloud": "Hydra Cloud", - "subscribe_now": "Inscreva-se agora", - "cloud_achievements": "Salvamento de conquistas em nuvem", - "animated_profile_picture": "Fotos de perfil animadas", - "premium_support": "Suporte Premium", - "show_and_compare_achievements": "Exiba e compare suas conquistas com outros usuários", - "animated_profile_banner": "Banner animado no perfil", - "cloud_saving": "Saves de jogos em nuvem", - "hydra_cloud_feature_found": "Você descobriu uma funcionalidade Hydra Cloud!", - "learn_more": "Saiba mais", - "debrid_description": "Baixe até 4x mais rápido com Nimbus" - } -} +{ + "language_name": "Português (Brasil)", + "app": { + "successfully_signed_in": "Autenticado com sucesso" + }, + "home": { + "featured": "Destaques", + "hot": "Populares", + "weekly": "📅 Mais baixados da semana", + "achievements": "🏆 Pra platinar", + "surprise_me": "Surpreenda-me", + "no_results": "Nenhum resultado encontrado", + "start_typing": "Comece a digitar para pesquisar…" + }, + "sidebar": { + "catalogue": "Catálogo", + "downloads": "Downloads", + "settings": "Ajustes", + "my_library": "Biblioteca", + "downloading_metadata": "{{title}} (Baixando metadados…)", + "paused": "{{title}} (Pausado)", + "downloading": "{{title}} ({{percentage}} - Baixando…)", + "filter": "Buscar", + "home": "Início", + "queued": "{{title}} (Na fila)", + "game_has_no_executable": "Jogo não possui executável selecionado", + "sign_in": "Login", + "friends": "Amigos", + "need_help": "Precisa de ajuda?", + "favorites": "Favoritos" + }, + "header": { + "search": "Buscar jogos", + "catalogue": "Catálogo", + "downloads": "Downloads", + "search_results": "Resultados da busca", + "settings": "Ajustes", + "home": "Início", + "version_available_install": "Versão {{version}} disponível. Clique aqui para reiniciar e instalar.", + "version_available_download": "Versão {{version}} disponível. Clique aqui para fazer o download." + }, + "bottom_panel": { + "no_downloads_in_progress": "Sem downloads em andamento", + "downloading_metadata": "Baixando metadados de {{title}}…", + "downloading": "Baixando {{title}}… ({{percentage}} concluído) - Conclusão {{eta}} - {{speed}}", + "calculating_eta": "Baixando {{title}}… ({{percentage}} concluído) - Calculando tempo restante…", + "checking_files": "Verificando arquivos de {{title}}…", + "installing_common_redist": "{{log}}…", + "installation_complete": "Instalação concluída", + "installation_complete_message": "Componentes recomendados instalados com sucesso" + }, + "game_details": { + "open_download_options": "Ver opções de download", + "download_options_zero": "Sem opções de download", + "download_options_one": "{{count}} opção de download", + "download_options_other": "{{count}} opções de download", + "updated_at": "Atualizado {{updated_at}}", + "resume": "Retomar", + "pause": "Pausar", + "cancel": "Cancelar", + "remove": "Remover", + "space_left_on_disk": "{{space}} livres em disco", + "eta": "Conclusão {{eta}}", + "calculating_eta": "Calculando tempo restante…", + "downloading_metadata": "Baixando metadados…", + "filter": "Filtrar repacks", + "requirements": "Requisitos de sistema", + "minimum": "Mínimos", + "recommended": "Recomendados", + "paused": "Pausado", + "release_date": "Lançado em {{date}}", + "publisher": "Publicado por {{publisher}}", + "hours": "horas", + "minutes": "minutos", + "amount_hours": "{{amount}} horas", + "amount_minutes": "{{amount}} minutos", + "accuracy": "{{accuracy}}% de precisão", + "add_to_library": "Adicionar à biblioteca", + "already_in_library": "Já está na biblioteca", + "remove_from_library": "Remover da biblioteca", + "no_downloads": "Nenhum download disponível", + "play_time": "Jogou por {{amount}}", + "next_suggestion": "Próxima sugestão", + "install": "Instalar", + "last_time_played": "Última sessão {{period}}", + "play": "Jogar", + "not_played_yet": "Você ainda não jogou {{title}}", + "close": "Fechar", + "deleting": "Excluindo instalador…", + "playing_now": "Jogando agora", + "change": "Explorar", + "repacks_modal_description": "Escolha o repack do jogo que deseja baixar", + "select_folder_hint": "Para trocar o diretório padrão, acesse a <0>Tela de Ajustes", + "download_now": "Iniciar download", + "no_shop_details": "Não foi possível obter os detalhes da loja.", + "download_options": "Opções de download", + "download_path": "Diretório de download", + "previous_screenshot": "Captura de tela anterior", + "next_screenshot": "Próxima captura de tela", + "screenshot": "Captura de tela {{number}}", + "open_screenshot": "Ver captura de tela {{number}}", + "download_settings": "Ajustes do download", + "downloader": "Downloader", + "select_executable": "Explorar", + "no_executable_selected": "Nenhum executável selecionado", + "open_folder": "Abrir pasta", + "open_download_location": "Ver arquivos baixados", + "create_shortcut": "Criar atalho na área de trabalho", + "remove_files": "Remover arquivos", + "options": "Gerenciar", + "remove_from_library_description": "Isso irá remover {{game}} da sua biblioteca", + "remove_from_library_title": "Tem certeza?", + "executable_section_title": "Executável", + "executable_section_description": "O caminho do arquivo que será executado ao clicar em \"Jogar\"", + "downloads_section_title": "Downloads", + "downloads_section_description": "Confira atualizações ou versões diferentes para este mesmo título", + "danger_zone_section_title": "Zona de perigo", + "danger_zone_section_description": "Remova o jogo da sua biblioteca ou os arquivos que foram baixados pelo Hydra", + "download_in_progress": "Download em andamento", + "download_paused": "Download pausado", + "last_downloaded_option": "Última opção baixada", + "create_steam_shortcut": "Criar atalho na Steam", + "create_shortcut_success": "Atalho criado com sucesso", + "you_might_need_to_restart_steam": "Você pode precisar reiniciar a Steam para ver as alterações", + "create_shortcut_error": "Erro ao criar atalho", + "nsfw_content_title": "Este jogo contém conteúdo inapropriado", + "nsfw_content_description": "{{title}} contém conteúdo que pode não ser apropriado para todas as idades. Você deseja continuar?", + "allow_nsfw_content": "Continuar", + "refuse_nsfw_content": "Voltar", + "stats": "Estatísticas", + "download_count": "Downloads", + "player_count": "Jogadores ativos", + "download_error": "Essa opção de download falhou", + "download": "Baixar", + "executable_path_in_use": "Executável em uso por \"{{game}}\"", + "warning": "Aviso:", + "hydra_needs_to_remain_open": "para este download, o Hydra precisa ficar aberto até a conclusão. Caso o Hydra encerre antes da conclusão, perderá seu progresso.", + "achievements": "Conquistas", + "achievements_count": "Conquistas ({{unlockedCount}}/{{achievementsCount}})", + "cloud_save": "Salvamento em nuvem", + "cloud_save_description": "Mantenha seu progresso na nuvem e continue de onde parou em qualquer dispositivo", + "backups": "Backups", + "install_backup": "Restaurar", + "delete_backup": "Apagar", + "create_backup": "Novo backup", + "last_backup_date": "Último backup em {{date}}", + "no_backup_preview": "Não foi possível encontrar nenhum salvamento para este jogo", + "restoring_backup": "Restaurando backup ({{progress}} concluído)…", + "uploading_backup": "Criando backup…", + "no_backups": "Você ainda não fez nenhum backup deste jogo", + "backup_uploaded": "Backup criado", + "backup_deleted": "Backup apagado", + "backup_restored": "Backup restaurado", + "see_all_achievements": "Ver todas as conquistas", + "sign_in_to_see_achievements": "Faça login para ver as conquistas", + "mapping_method_automatic": "Automático", + "mapping_method_manual": "Manual", + "mapping_method_label": "Método de mapeamento", + "files_automatically_mapped": "Arquivos automaticamente mapeados", + "no_backups_created": "Nenhum backup criado para este jogo", + "manage_files": "Gerenciar arquivos", + "loading_save_preview": "Buscando por arquivos de salvamento…", + "wine_prefix": "Prefixo Wine", + "wine_prefix_description": "O prefixo Wine que foi utilizado para instalar o jogo", + "launch_options": "Opções de Inicialização", + "launch_options_description": "Usuários avançados podem adicionar opções de inicialização no jogo (experimental)", + "launch_options_placeholder": "Nenhum parâmetro informado", + "no_download_option_info": "Sem informações disponíveis", + "backup_deletion_failed": "Falha ao apagar backup", + "max_number_of_artifacts_reached": "Número máximo de backups atingido para este jogo", + "achievements_not_sync": "Veja como exibir suas conquistas no perfil", + "backup_from": "Backup de {{date}}", + "automatic_backup_from": "Backup automático de {{date}}", + "enable_automatic_cloud_sync": "Habilitar sincronização automática na nuvem", + "custom_backup_location_set": "Localização customizada selecionada", + "select_folder": "Selecione a pasta", + "manage_files_description": "Gerencie quais arquivos serão feitos backup", + "clear": "Limpar", + "no_directory_selected": "Nenhum diretório selecionado", + "reset_achievements": "Resetar conquistas", + "reset_achievements_description": "Isso irá resetar todas as conquistas de {{game}}", + "reset_achievements_title": "Tem certeza?", + "reset_achievements_success": "Conquistas resetadas com sucesso", + "reset_achievements_error": "Falha ao resetar conquistas", + "no_write_permission": "Não é possível baixar nesse diretório. Clique aqui para saber mais.", + "download_error_gofile_quota_exceeded": "Você excedeu sua cota mensal do Gofile. Por favor, aguarde a cota resetar.", + "download_error_real_debrid_account_not_authorized": "Sua conta do Real-Debrid não está autorizada a fazer novos downloads. Por favor, verifique sua assinatura e tente novamente.", + "download_error_not_cached_on_real_debrid": "Este download não está disponível no Real-Debrid e a verificação do status do download não está disponível.", + "download_error_not_cached_on_torbox": "Este download não está disponível no TorBox e a verificação do status do download não está disponível.", + "download_error_not_cached_on_hydra": "Este download não está disponível no Nimbus.", + "game_removed_from_favorites": "Jogo removido dos favoritos", + "game_added_to_favorites": "Jogo adicionado aos favoritos", + "automatically_extract_downloaded_files": "Extrair automaticamente os arquivos baixados", + "create_start_menu_shortcut": "Criar atalho no Menu Iniciar", + "invalid_wine_prefix_path": "Caminho do prefixo Wine inválido", + "invalid_wine_prefix_path_description": "O caminho para o prefixo Wine é inválido. Por favor, verifique o caminho e tente novamente.", + "artifact_renamed": "Backup renomeado com sucesso", + "rename_artifact": "Renomear Backup", + "rename_artifact_description": "Renomeie o backup para um nome mais descritivo", + "artifact_name_label": "Nome do backup", + "artifact_name_placeholder": "Insira um nome para o backup", + "save_changes": "Salvar mudanças", + "required_field": "Este campo é obrigatório", + "max_length_field": "Este campo deve ter menos de {{length}} caracteres", + "freeze_backup": "Fixar para não ser apagado por backups automáticos", + "unfreeze_backup": "Remover dos fixados", + "backup_frozen": "Backup fixado", + "backup_unfrozen": "Backup removido dos fixados", + "backup_freeze_failed": "Falha ao fixar backup", + "backup_freeze_failed_description": "Você deve deixar pelo menos um espaço livre para backups automáticos", + "game_details": "Detalhes do Jogo", + "currency_symbol": "R$", + "currency_country": "br", + "prices": "Preços", + "no_prices_found": "Nenhum preço encontrado", + "retail_price": "Preço de lojas oficiais", + "keyshop_price": "Preço em keyshops", + "historical_retail": "Preço histórico de lojas oficiais", + "historical_keyshop": "Preço histórico em keyshops", + "supported_languages": "Idiomas suportados", + "language": "Idioma", + "caption": "Legenda", + "audio": "Áudio" + }, + "activation": { + "title": "Ativação", + "installation_id": "ID da instalação:", + "enter_activation_code": "Insira seu código de ativação", + "message": "Se você não sabe onde conseguir o código, talvez você não devesse estar aqui.", + "activate": "Ativar", + "loading": "Carregando…" + }, + "downloads": { + "resume": "Retomar", + "pause": "Pausar", + "eta": "Conclusão {{eta}}", + "paused": "Pausado", + "verifying": "Verificando…", + "completed": "Concluído", + "removed": "Cancelado", + "cancel": "Cancelar", + "filter": "Filtrar jogos baixados", + "remove": "Remover", + "downloading_metadata": "Baixando metadados…", + "delete": "Remover instalador", + "delete_modal_description": "Isso removerá todos os arquivos de instalação do seu computador", + "delete_modal_title": "Tem certeza?", + "deleting": "Excluindo instalador…", + "install": "Instalar", + "download_in_progress": "Baixando agora", + "queued_downloads": "Na fila", + "downloads_completed": "Concluído", + "queued": "Na fila", + "no_downloads_title": "Nada por aqui…", + "no_downloads_description": "Você ainda não baixou nada pelo Hydra, mas nunca é tarde para começar.", + "checking_files": "Verificando arquivos…", + "seeding": "Semeando", + "stop_seeding": "Parar de semear", + "resume_seeding": "Semear", + "options": "Gerenciar", + "extract": "Extrair arquivos", + "extracting": "Extraindo arquivos…" + }, + "settings": { + "downloads_path": "Diretório dos downloads", + "change": "Explorar...", + "notifications": "Notificações", + "enable_download_notifications": "Quando um download for concluído", + "gg_deals_api_key_description": "gg deals api key. Usado para mostrar o menor preço. (https://gg.deals/api/)", + "enable_repack_list_notifications": "Quando a lista de repacks for atualizada", + "real_debrid_api_token_label": "Token de API do Real-Debrid", + "quit_app_instead_hiding": "Encerrar o Hydra em vez de apenas minimizá-lo ao fechar", + "launch_with_system": "Iniciar o Hydra junto com o sistema", + "general": "Geral", + "behavior": "Comportamento", + "download_sources": "Fontes de download", + "language": "Idioma", + "api_token": "Token de API", + "enable_real_debrid": "Habilitar Real-Debrid", + "debrid_api_token_hint": "Você pode obter seu token de API <0>aqui", + "real_debrid_description": "O Real-Debrid é um downloader sem restrições que permite baixar arquivos instantaneamente e com a melhor velocidade da sua Internet.", + "debrid_invalid_token": "Token de API inválido", + "real_debrid_free_account_error": "A conta \"{{username}}\" é uma conta gratuita. Por favor, assine a Real-Debrid", + "debrid_linked_message": "Conta \"{{username}}\" vinculada", + "save_changes": "Salvar mudanças", + "changes_saved": "Ajustes salvos com sucesso", + "download_sources_description": "Hydra vai buscar links de download em todas as fontes habilitadas. A URL da fonte deve ser um link direto para um arquivo .json contendo uma lista de links.", + "validate_download_source": "Validar", + "remove_download_source": "Remover", + "add_download_source": "Adicionar fonte", + "download_count_zero": "Sem downloads na lista", + "download_count_one": "{{countFormatted}} download na lista", + "download_count_other": "{{countFormatted}} downloads na lista", + "download_source_url": "URL da fonte", + "add_download_source_description": "Insira a URL contendo o arquivo .json", + "download_source_up_to_date": "Sincronizada", + "download_source_errored": "Falhou", + "sync_download_sources": "Sincronizar", + "removed_download_source": "Fonte removida", + "removed_download_sources": "Fontes removidas", + "cancel_button_confirmation_delete_all_sources": "Não", + "confirm_button_confirmation_delete_all_sources": "Sim, excluir tudo", + "title_confirmation_delete_all_sources": "Remover todas as fontes de download", + "description_confirmation_delete_all_sources": "Você irá remover todas as fontes de download. Deseja prosseguir?", + "button_delete_all_sources": "Remover todas", + "added_download_source": "Fonte adicionada", + "download_sources_synced": "As fontes foram sincronizadas", + "insert_valid_json_url": "Insira a url de um JSON válido", + "found_download_option_zero": "Nenhuma opção de download encontrada", + "found_download_option_one": "{{countFormatted}} opção de download encontrada", + "found_download_option_other": "{{countFormatted}} opções de download encontradas", + "import": "Importar", + "privacy": "Privacidade", + "private": "Privado", + "friends_only": "Apenas amigos", + "public": "Público", + "profile_visibility": "Visibilidade do perfil", + "profile_visibility_description": "Escolha quem pode ver seu perfil e biblioteca", + "required_field": "Este campo é obrigatório", + "source_already_exists": "Essa fonte já foi adicionada", + "must_be_valid_url": "A fonte deve ser uma URL válida", + "blocked_users": "Usuários bloqueados", + "user_unblocked": "Usuário desbloqueado", + "enable_achievement_notifications": "Quando uma conquista é desbloqueada", + "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", + "account": "Conta", + "no_users_blocked": "Você não bloqueou nenhum usuário", + "subscription_active_until": "Sua assinatura Hydra Cloud ficará ativa até {{date}}", + "manage_subscription": "Gerenciar assinatura", + "update_email": "Atualizar email", + "update_password": "Atualizar senha", + "current_email": "Email atual:", + "no_email_account": "Você ainda não adicionou um email a sua conta", + "account_data_updated_successfully": "Dados da conta atualizados com sucesso", + "renew_subscription": "Renovar Hydra Cloud", + "subscription_expired_at": "Sua assinatura expirou em {{date}}", + "no_subscription": "Aproveite o Hydra da melhor forma possível", + "become_subscriber": "Seja Hydra Cloud", + "subscription_renew_cancelled": "A renovação automática está desativada", + "subscription_renews_on": "Sua assinatura renova dia {{date}}", + "bill_sent_until": "Sua próxima cobrança será enviada até esse dia", + "no_themes": "Parece que você ainda não tem nenhum tema. Não se preocupe, clique aqui para criar sua primeira obra de arte.", + "editor_tab_save": "Salvar", + "web_store": "Loja de temas", + "clear_themes": "Limpar", + "create_theme": "Criar", + "create_theme_modal_title": "Criar tema customizado", + "create_theme_modal_description": "Criar novo tema para customizar a aparência do Hydra", + "theme_name": "Nome", + "insert_theme_name": "Insira o nome do tema", + "set_theme": "Habilitar tema", + "unset_theme": "Desabilitar tema", + "delete_theme": "Deletar tema", + "edit_theme": "Editar tema", + "delete_all_themes": "Deletar todos os temas", + "delete_all_themes_description": "Isso irá deletar todos os seus temas", + "delete_theme_description": "Isso irá deletar o tema {{theme}}", + "cancel": "Cancelar", + "appearance": "Aparência", + "enable_torbox": "Habilitar TorBox", + "torbox_description": "TorBox é o seu serviço de seedbox premium que rivaliza até com os melhores servidores do mercado.", + "torbox_account_linked": "Conta do TorBox vinculada", + "create_real_debrid_account": "Clique aqui se você ainda não tem uma conta do Real-Debrid", + "create_torbox_account": "Clique aqui se você ainda não tem uma conta do TorBox", + "real_debrid_account_linked": "Conta Real-Debrid associada", + "name_min_length": "O nome do tema deve ter pelo menos 3 caracteres", + "import_theme": "Importar tema", + "import_theme_description": "Você irá importar {{theme}} da loja de temas", + "error_importing_theme": "Erro ao importar tema", + "theme_imported": "Tema importado com sucesso", + "enable_friend_request_notifications": "Quando um pedido de amizade é recebido", + "enable_auto_install": "Baixar atualizações automaticamente", + "common_redist": "Componentes recomendados", + "common_redist_description": "Componentes recomendados são necessários para executar alguns jogos. A instalação deles é recomendada para evitar problemas.", + "install_common_redist": "Instalar", + "installing_common_redist": "Instalando…", + "show_download_speed_in_megabytes": "Exibir taxas de download em megabytes por segundo", + "extract_files_by_default": "Extrair arquivos automaticamente após o download", + "enable_steam_achievements": "Habilitar busca por conquistas da Steam", + "enable_achievement_custom_notifications": "Habilitar notificações customizadas de conquistas", + "top-left": "Superior esquerdo", + "top-center": "Superior central", + "top-right": "Superior direito", + "bottom-left": "Inferior esquerdo", + "bottom-right": "Inferior direito", + "bottom-center": "Inferior central", + "achievement_custom_notification_position": "Posição das notificações customizadas de conquista", + "alignment": "Alinhamento", + "variation": "Variação", + "default": "Padrão", + "rare": "Rara", + "platinum": "Platina", + "hidden": "Oculta", + "test_notification": "Testar notificação", + "notification_preview": "Prévia da Notificação de Conquistas", + "enable_friend_start_game_notifications": "Quando um amigo iniciar um jogo" + }, + "notifications": { + "download_complete": "Download concluído", + "game_ready_to_install": "{{title}} está pronto para ser instalado", + "repack_list_updated": "Lista de repacks atualizada", + "repack_count_one": "{{count}} novo repack", + "repack_count_other": "{{count}} novos repacks", + "new_update_available": "Versão {{version}} disponível", + "restart_to_install_update": "Reinicie o Hydra para instalar a nova versão", + "new_friend_request_title": "Novo pedido de amizade", + "new_friend_request_description": "{{displayName}} te enviou um pedido de amizade", + "extraction_complete": "Extração concluída", + "game_extracted": "{{title}} extraído com sucesso", + "friend_started_playing_game": "{{displayName}} começou a jogar", + "test_achievement_notification_title": "Esta é uma notificação de teste", + "test_achievement_notification_description": "Bem legal, né?" + }, + "system_tray": { + "open": "Abrir Hydra", + "quit": "Fechar" + }, + "game_card": { + "available_one": "Disponível", + "available_other": "Disponíveis", + "no_downloads": "Sem downloads disponíveis" + }, + "binary_not_found_modal": { + "title": "Programas não instalados", + "description": "Os executáveis do Wine ou Lutris não foram encontrados em seu sistema.", + "instructions": "Verifique a forma correta de instalar algum deles no seu distro Linux, garantindo assim a execução normal do jogo" + }, + "catalogue": { + "search": "Filtrar…", + "developers": "Desenvolvedores", + "genres": "Gêneros", + "tags": "Marcadores", + "publishers": "Distribuidoras", + "download_sources": "Fontes de download", + "result_count": "{{resultCount}} resultados", + "filter_count": "{{filterCount}} disponíveis", + "clear_filters": "Limpar {{filterCount}} selecionados" + }, + "modal": { + "close": "Botão de fechar" + }, + "forms": { + "toggle_password_visibility": "Alternar visibilidade da senha" + }, + "user_profile": { + "amount_hours": "{{amount}} horas", + "amount_minutes": "{{amount}} minutos", + "last_time_played": "Última sessão {{period}}", + "activity": "Atividades recentes", + "library": "Biblioteca", + "total_play_time": "Tempo total de jogo", + "no_recent_activity_title": "Hmmm… nada por aqui", + "no_recent_activity_description": "Parece que você não jogou nada recentemente. Que tal começar agora?", + "display_name": "Nome de exibição", + "saving": "Salvando…", + "save": "Salvar", + "edit_profile": "Editar perfil", + "saved_successfully": "Salvo com sucesso", + "try_again": "Por favor, tente novamente", + "cancel": "Cancelar", + "successfully_signed_out": "Deslogado com sucesso", + "sign_out": "Sair da conta", + "sign_out_modal_title": "Deseja mesmo sair?", + "playing_for": "Jogando por {{amount}}", + "sign_out_modal_text": "Sua biblioteca de jogos está associada com a sua conta atual. Ao sair, sua biblioteca não aparecerá mais no Hydra e qualquer progresso não será salvo. Deseja continuar?", + "add_friends": "Adicionar Amigos", + "friend_code": "Código de amigo", + "see_profile": "Ver perfil", + "friend_request_sent": "Pedido de amizade enviado", + "friends": "Amigos", + "add": "Adicionar", + "sending": "Enviando", + "friends_list": "Lista de amigos", + "user_not_found": "Usuário não encontrado", + "block_user": "Bloquear", + "add_friend": "Adicionar amigo", + "request_sent": "Pedido enviado", + "request_received": "Pedido recebido", + "accept_request": "Aceitar pedido", + "ignore_request": "Ignorar pedido", + "cancel_request": "Cancelar pedido", + "undo_friendship": "Desfazer amizade", + "request_accepted": "Pedido de amizade aceito", + "user_blocked_successfully": "Usuário bloqueado com sucesso", + "user_block_modal_text": "Bloquear {{displayName}}", + "blocked_users": "Usuários bloqueados", + "unblock": "Desbloquear", + "no_friends_added": "Você ainda não possui amigos adicionados", + "pending": "Pendentes", + "no_pending_invites": "Você não possui convites de amizade pendentes", + "no_blocked_users": "Você não tem nenhum usuário bloqueado", + "friend_code_copied": "Código de amigo copiado", + "undo_friendship_modal_text": "Isso irá remover sua amizade com {{displayName}}", + "privacy_hint": "Pra controlar quem pode ver seu perfil, acesse a <0>Tela de Configurações", + "image_process_failure": "Falha ao processar a imagem", + "required_field": "Este campo é obrigatório", + "displayname_min_length": "Nome de exibição deve ter pelo menos 3 caracteres", + "displayname_max_length": "Nome de exibição deve ter no máximo 50 caracteres", + "locked_profile": "Este perfil é privado", + "report_profile": "Reportar este perfil", + "report_reason": "Por que você deseja reportar este perfil?", + "report_description": "Informações adicionais", + "report_description_placeholder": "Insira aqui", + "report": "Reportar", + "report_reason_hate": "Discurso de ódio", + "report_reason_sexual_content": "Conteúdo sexual", + "report_reason_violence": "Violência", + "report_reason_spam": "Spam", + "report_reason_other": "Outro", + "profile_reported": "Perfil reportado", + "your_friend_code": "Seu código de amigo:", + "upload_banner": "Carregar banner", + "uploading_banner": "Carregando banner…", + "background_image_updated": "Imagem de fundo salva", + "stats": "Estatísticas", + "achievements": "conquistas", + "games": "Jogos", + "ranking_updated_weekly": "O ranking é atualizado semanalmente", + "playing": "Jogando {{game}}", + "achievements_unlocked": "Conquistas desbloqueadas", + "earned_points": "Pontos ganhos", + "show_achievements_on_profile": "Exiba suas conquistas no perfil", + "show_points_on_profile": "Exiba seus pontos ganhos no perfil", + "error_adding_friend": "Não foi possível enviar o pedido de amizade. Verifique o código de amizade inserido", + "friend_code_length_error": "Código de amigo deve ter 8 caracteres" + }, + "achievement": { + "achievement_unlocked": "Conquista desbloqueada", + "your_achievements": "Suas Conquistas", + "user_achievements": "Conquistas de {{displayName}}", + "unlocked_at": "Desbloqueada em: {{date}}", + "subscription_needed": "Você precisa de uma assinatura Hydra Cloud para visualizar este conteúdo", + "new_achievements_unlocked": "{{achievementCount}} novas conquistas de {{gameCount}} jogos", + "achievement_progress": "{{unlockedCount}}/{{totalCount}} conquistas", + "achievements_unlocked_for_game": "Desbloqueadas {{achievementCount}} novas conquistas em {{gameTitle}}", + "hidden_achievement_tooltip": "Esta é uma conquista oculta", + "achievement_earn_points": "Ganhe {{points}} pontos com essa conquista", + "earned_points": "Pontos ganhos:", + "available_points": "Pontos disponíveis:", + "how_to_earn_achievements_points": "Como desbloquear pontos nas conquistas?" + }, + "hydra_cloud": { + "subscription_tour_title": "Assinatura Hydra Cloud", + "hydra_cloud": "Hydra Cloud", + "subscribe_now": "Inscreva-se agora", + "cloud_achievements": "Salvamento de conquistas em nuvem", + "animated_profile_picture": "Fotos de perfil animadas", + "premium_support": "Suporte Premium", + "show_and_compare_achievements": "Exiba e compare suas conquistas com outros usuários", + "animated_profile_banner": "Banner animado no perfil", + "cloud_saving": "Saves de jogos em nuvem", + "hydra_cloud_feature_found": "Você descobriu uma funcionalidade Hydra Cloud!", + "learn_more": "Saiba mais", + "debrid_description": "Baixe até 4x mais rápido com Nimbus" + } +} diff --git a/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx b/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx index 8d98f213..f67e4dfa 100755 --- a/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx @@ -1,55 +1,66 @@ -import { useContext } from "react"; -import { useTranslation } from "react-i18next"; -import { gameDetailsContext } from "@renderer/context/game-details/game-details.context"; -import { SidebarSection } from "../sidebar-section/sidebar-section"; - -export function GameLanguageSection() { - const { t } = useTranslation("game_details"); - const { shopDetails, objectId } = useContext(gameDetailsContext); - - const getLanguages = () => { - let languages = shopDetails?.supported_languages; - if (!languages) return []; - languages = languages?.split('
')[0]; - let arrayIdiomas = languages?.split(',') - let listLanguages: { language: string; caption: string; audio: string }[] = []; - arrayIdiomas?.forEach((lang) => { - const objectLanguage = { - language: lang.replace("*", ""), - caption: "✔", - audio: lang.includes("*") ? "✔" : "" }; - listLanguages.push(objectLanguage); - }) - return listLanguages; - } - - - return ( - -
-

{t("supported_languages")}

- - - - - - - - - - {getLanguages().map((lang) => ( - - - - - - ))} - -
{t("language")}{t("caption")}{t("audio")}
{lang.language}{lang.caption}{lang.audio}
-
- -
- ); -} \ No newline at end of file +import { useContext } from "react"; +import { useTranslation } from "react-i18next"; +import { gameDetailsContext } from "@renderer/context/game-details/game-details.context"; +import { SidebarSection } from "../sidebar-section/sidebar-section"; + +export function GameLanguageSection() { + const { t } = useTranslation("game_details"); + const { shopDetails, objectId } = useContext(gameDetailsContext); + + const getLanguages = () => { + let languages = shopDetails?.supported_languages; + if (!languages) return []; + languages = languages?.split("
")[0]; + const arrayIdiomas = languages?.split(","); + const listLanguages: { + language: string; + caption: string; + audio: string; + }[] = []; + arrayIdiomas?.forEach((lang) => { + const objectLanguage = { + language: lang.replace("*", ""), + caption: "✔", + audio: lang.includes("*") ? "✔" : "", + }; + listLanguages.push(objectLanguage); + }); + return listLanguages; + }; + + return ( + +
+

{t("supported_languages")}

+ + + + + + + + + + {getLanguages().map((lang) => ( + + + + + + ))} + +
{t("language")}{t("caption")}{t("audio")}
{lang.language}{lang.caption}{lang.audio}
+
+ +
+ ); +} diff --git a/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx b/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx index 3f7ff29c..44560459 100755 --- a/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx @@ -1,62 +1,86 @@ -import { useCallback, useContext, useEffect, useState } from "react"; -import { SidebarSection } from "../sidebar-section/sidebar-section"; -import { useTranslation } from "react-i18next"; -import { gameDetailsContext } from "@renderer/context/game-details/game-details.context"; -import { useAppSelector } from "@renderer/hooks"; - -export function GamePricesSection() { - const userPreferences = useAppSelector((state) => state.userPreferences.value); - const { t } = useTranslation("game_details"); - const [priceData, setPriceData] = useState(null); - const [isLoadingPrices, setIsLoadingPrices] = useState(false); - const { objectId } = useContext(gameDetailsContext); - - const fetchGamePrices = useCallback(async (steamAppId: string) => { - setIsLoadingPrices(true); - try { - const apiKey = userPreferences?.ggDealsApiKey || import.meta.env.VITE_GG_DEALS_API_KEY; - if (!apiKey) { - setPriceData(null); - setIsLoadingPrices(false); - return; - } - const url = `${import.meta.env.VITE_GG_DEALS_API_URL}/?ids=${steamAppId}&key=${apiKey}®ion=br`; - const response = await fetch(url); - if (!response.ok) { - throw new Error("Network response was not ok"); - } - const data = await response.json(); - setPriceData(data.data?.[steamAppId] ?? null); - } catch (error) { - setPriceData(null); - } finally { - setIsLoadingPrices(false); - } - }, []); - - useEffect(() => { - if (objectId) { - fetchGamePrices(objectId.toString()); - } - }, [objectId, fetchGamePrices]); - - return ( - - {isLoadingPrices ? ( -
{t("loading")}
- ) : priceData ? ( -
-
    -
  • {t("retail_price")}: {t("currency_symbol")}{priceData.prices.currentRetail}
  • -
  • {t("keyshop_price")}: {t("currency_symbol")}{priceData.prices.currentKeyshops}
  • -
  • {t("historical_retail")}: {t("currency_symbol")}{priceData.prices.historicalRetail}
  • -
  • {t("historical_keyshop")}: {t("currency_symbol")}{priceData.prices.historicalKeyshops}
  • -
  • clique para ver todos os preços
  • -
-
- ) : ( -
{t("no_prices_found")}
- )} -
- ); -} \ No newline at end of file +import { useCallback, useContext, useEffect, useState } from "react"; +import { SidebarSection } from "../sidebar-section/sidebar-section"; +import { useTranslation } from "react-i18next"; +import { gameDetailsContext } from "@renderer/context/game-details/game-details.context"; +import { useAppSelector } from "@renderer/hooks"; + +export function GamePricesSection() { + const userPreferences = useAppSelector( + (state) => state.userPreferences.value + ); + const { t } = useTranslation("game_details"); + const [priceData, setPriceData] = useState(null); + const [isLoadingPrices, setIsLoadingPrices] = useState(false); + const { objectId } = useContext(gameDetailsContext); + + const fetchGamePrices = useCallback(async (steamAppId: string) => { + setIsLoadingPrices(true); + try { + const apiKey = + userPreferences?.ggDealsApiKey || import.meta.env.VITE_GG_DEALS_API_KEY; + if (!apiKey) { + setPriceData(null); + setIsLoadingPrices(false); + return; + } + const url = `${import.meta.env.VITE_GG_DEALS_API_URL}/?ids=${steamAppId}&key=${apiKey}®ion=br`; + const response = await fetch(url); + if (!response.ok) { + throw new Error("Network response was not ok"); + } + const data = await response.json(); + setPriceData(data.data?.[steamAppId] ?? null); + } catch (error) { + setPriceData(null); + } finally { + setIsLoadingPrices(false); + } + }, []); + + useEffect(() => { + if (objectId) { + fetchGamePrices(objectId.toString()); + } + }, [objectId, fetchGamePrices]); + + return ( + + {isLoadingPrices ? ( +
{t("loading")}
+ ) : priceData ? ( +
+
    +
  • + {t("retail_price")}: {t("currency_symbol")} + {priceData.prices.currentRetail} +
  • +
  • + {t("keyshop_price")}: {t("currency_symbol")} + {priceData.prices.currentKeyshops} +
  • +
  • + {t("historical_retail")}: {t("currency_symbol")} + {priceData.prices.historicalRetail} +
  • +
  • + {t("historical_keyshop")}: {t("currency_symbol")} + {priceData.prices.historicalKeyshops} +
  • +
  • + + clique para ver todos os preços + +
  • +
+
+ ) : ( +
{t("no_prices_found")}
+ )} +
+ ); +} diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.scss b/src/renderer/src/pages/game-details/sidebar/sidebar.scss index 1f25b151..84386f12 100755 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.scss +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.scss @@ -1,217 +1,218 @@ -@use "../../../scss/globals.scss"; - -.content-sidebar { - border-left: solid 1px globals.$border-color; - background-color: globals.$dark-background-color; - width: 100%; - height: 100%; - - @media (min-width: 1024px) { - max-width: 300px; - width: 100%; - } - - @media (min-width: 1280px) { - width: 100%; - max-width: 400px; - } -} - -.requirement { - &__button-container { - width: 100%; - display: flex; - } - - &__button { - border: solid 1px globals.$border-color; - border-left: none; - border-right: none; - border-radius: 0; - width: 100%; - } - - &__details { - padding: calc(globals.$spacing-unit * 2); - line-height: 22px; - font-size: globals.$body-font-size; - - a { - display: flex; - color: globals.$body-color; - } - } - - &__details-skeleton { - display: flex; - flex-direction: column; - gap: globals.$spacing-unit; - padding: calc(globals.$spacing-unit * 2); - font-size: globals.$body-font-size; - } -} - -.how-long-to-beat { - &__categories-list { - margin: 0; - padding: calc(globals.$spacing-unit * 2); - display: flex; - flex-direction: column; - gap: calc(globals.$spacing-unit * 2); - } - - &__category { - display: flex; - flex-direction: column; - gap: calc(globals.$spacing-unit / 2); - background: linear-gradient( - 90deg, - transparent 20%, - rgb(255 255 255 / 2%) 100% - ); - border-radius: 4px; - padding: globals.$spacing-unit calc(globals.$spacing-unit * 2); - border: solid 1px globals.$border-color; - } - - &__category-label { - color: globals.$muted-color; - } - - &__category-label--bold { - font-weight: bold; - } - - &__category-skeleton { - border: solid 1px globals.$border-color; - border-radius: 4px; - height: 76px; - } -} - -.stats { - &__section { - display: flex; - gap: calc(globals.$spacing-unit * 2); - padding: calc(globals.$spacing-unit * 2); - justify-content: space-between; - transition: max-height ease 0.5s; - overflow: hidden; - - @media (min-width: 1024px) { - flex-direction: column; - } - - @media (min-width: 1280px) { - flex-direction: row; - } - } - - &__category-title { - font-size: globals.$small-font-size; - font-weight: bold; - display: flex; - align-items: center; - gap: globals.$spacing-unit; - } - - &__category { - display: flex; - flex-direction: row; - gap: calc(globals.$spacing-unit / 2); - justify-content: space-between; - align-items: center; - } -} - -.list { - list-style: none; - margin: 0; - display: flex; - flex-direction: column; - gap: calc(globals.$spacing-unit * 2); - padding: calc(globals.$spacing-unit * 2); - - &__item { - display: flex; - cursor: pointer; - transition: all ease 0.1s; - color: globals.$muted-color; - width: 100%; - overflow: hidden; - border-radius: 4px; - padding: globals.$spacing-unit; - gap: calc(globals.$spacing-unit * 2); - align-items: center; - text-align: left; - - &:hover { - background-color: rgba(255, 255, 255, 0.15); - text-decoration: none; - } - } - - &__item-image { - width: 54px; - height: 54px; - border-radius: 4px; - object-fit: cover; - - &--locked { - filter: grayscale(100%); - } - } -} - -.subscription-required-button { - text-decoration: none; - display: flex; - justify-content: center; - width: 100%; - gap: calc(globals.$spacing-unit / 2); - color: globals.$warning-color; - cursor: pointer; - - &:hover { - text-decoration: underline; - } -} - -.achievements-placeholder { - position: absolute; - z-index: 1; - inset: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.7); - display: flex; - justify-content: center; - align-items: center; - flex-direction: column; - gap: 8px; -} - -.achievements-placeholder__blur { - filter: blur(4px); -} - -.table-languages { - width: 100%; - border-collapse: collapse; - text-align: left; - - th, td { - padding: globals.$spacing-unit; - border-bottom: solid 1px globals.$border-color; - } - - th { - font-size: globals.$small-font-size; - color: globals.$muted-color; - font-weight: normal; - } - - td { - font-size: globals.$body-font-size; - } -} \ No newline at end of file +@use "../../../scss/globals.scss"; + +.content-sidebar { + border-left: solid 1px globals.$border-color; + background-color: globals.$dark-background-color; + width: 100%; + height: 100%; + + @media (min-width: 1024px) { + max-width: 300px; + width: 100%; + } + + @media (min-width: 1280px) { + width: 100%; + max-width: 400px; + } +} + +.requirement { + &__button-container { + width: 100%; + display: flex; + } + + &__button { + border: solid 1px globals.$border-color; + border-left: none; + border-right: none; + border-radius: 0; + width: 100%; + } + + &__details { + padding: calc(globals.$spacing-unit * 2); + line-height: 22px; + font-size: globals.$body-font-size; + + a { + display: flex; + color: globals.$body-color; + } + } + + &__details-skeleton { + display: flex; + flex-direction: column; + gap: globals.$spacing-unit; + padding: calc(globals.$spacing-unit * 2); + font-size: globals.$body-font-size; + } +} + +.how-long-to-beat { + &__categories-list { + margin: 0; + padding: calc(globals.$spacing-unit * 2); + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit * 2); + } + + &__category { + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit / 2); + background: linear-gradient( + 90deg, + transparent 20%, + rgb(255 255 255 / 2%) 100% + ); + border-radius: 4px; + padding: globals.$spacing-unit calc(globals.$spacing-unit * 2); + border: solid 1px globals.$border-color; + } + + &__category-label { + color: globals.$muted-color; + } + + &__category-label--bold { + font-weight: bold; + } + + &__category-skeleton { + border: solid 1px globals.$border-color; + border-radius: 4px; + height: 76px; + } +} + +.stats { + &__section { + display: flex; + gap: calc(globals.$spacing-unit * 2); + padding: calc(globals.$spacing-unit * 2); + justify-content: space-between; + transition: max-height ease 0.5s; + overflow: hidden; + + @media (min-width: 1024px) { + flex-direction: column; + } + + @media (min-width: 1280px) { + flex-direction: row; + } + } + + &__category-title { + font-size: globals.$small-font-size; + font-weight: bold; + display: flex; + align-items: center; + gap: globals.$spacing-unit; + } + + &__category { + display: flex; + flex-direction: row; + gap: calc(globals.$spacing-unit / 2); + justify-content: space-between; + align-items: center; + } +} + +.list { + list-style: none; + margin: 0; + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit * 2); + padding: calc(globals.$spacing-unit * 2); + + &__item { + display: flex; + cursor: pointer; + transition: all ease 0.1s; + color: globals.$muted-color; + width: 100%; + overflow: hidden; + border-radius: 4px; + padding: globals.$spacing-unit; + gap: calc(globals.$spacing-unit * 2); + align-items: center; + text-align: left; + + &:hover { + background-color: rgba(255, 255, 255, 0.15); + text-decoration: none; + } + } + + &__item-image { + width: 54px; + height: 54px; + border-radius: 4px; + object-fit: cover; + + &--locked { + filter: grayscale(100%); + } + } +} + +.subscription-required-button { + text-decoration: none; + display: flex; + justify-content: center; + width: 100%; + gap: calc(globals.$spacing-unit / 2); + color: globals.$warning-color; + cursor: pointer; + + &:hover { + text-decoration: underline; + } +} + +.achievements-placeholder { + position: absolute; + z-index: 1; + inset: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + gap: 8px; +} + +.achievements-placeholder__blur { + filter: blur(4px); +} + +.table-languages { + width: 100%; + border-collapse: collapse; + text-align: left; + + th, + td { + padding: globals.$spacing-unit; + border-bottom: solid 1px globals.$border-color; + } + + th { + font-size: globals.$small-font-size; + color: globals.$muted-color; + font-weight: normal; + } + + td { + font-size: globals.$body-font-size; + } +} diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx index e52a7126..af72dc92 100755 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx @@ -1,273 +1,273 @@ -import { useContext, useEffect, useState } from "react"; -import type { - HowLongToBeatCategory, - SteamAppDetails, - UserAchievement, -} from "@types"; -import { useTranslation } from "react-i18next"; -import { Button, Link } from "@renderer/components"; - -import { gameDetailsContext } from "@renderer/context"; -import { useDate, useFormat, useUserDetails } from "@renderer/hooks"; -import { - CloudOfflineIcon, - DownloadIcon, - LockIcon, - PeopleIcon, -} from "@primer/octicons-react"; -import { HowLongToBeatSection } from "./how-long-to-beat-section"; -import { howLongToBeatEntriesTable } from "@renderer/dexie"; -import { SidebarSection } from "../sidebar-section/sidebar-section"; -import { buildGameAchievementPath } from "@renderer/helpers"; -import { useSubscription } from "@renderer/hooks/use-subscription"; -import "./sidebar.scss"; -import { GamePricesSection } from "./game-prices-section"; -import { GameLanguageSection } from "./game-language-section"; - -const achievementsPlaceholder: UserAchievement[] = [ - { - displayName: "Timber!!", - name: "1", - hidden: false, - description: "Chop down your first tree.", - icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0fbb33098c9da39d1d4771d8209afface9c46e81.jpg", - icongray: - "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0fbb33098c9da39d1d4771d8209afface9c46e81.jpg", - unlocked: true, - unlockTime: Date.now(), - }, - { - displayName: "Supreme Helper Minion!", - name: "2", - hidden: false, - icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0a6ff6a36670c96ceb4d30cf6fd69d2fdf55f38e.jpg", - icongray: - "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0a6ff6a36670c96ceb4d30cf6fd69d2fdf55f38e.jpg", - unlocked: false, - unlockTime: null, - }, - { - displayName: "Feast of Midas", - name: "3", - hidden: false, - icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/2d10311274fe7c92ab25cc29afdca86b019ad472.jpg", - icongray: - "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/2d10311274fe7c92ab25cc29afdca86b019ad472.jpg", - unlocked: false, - unlockTime: null, - }, -]; - -export function Sidebar() { - const [howLongToBeat, setHowLongToBeat] = useState<{ - isLoading: boolean; - data: HowLongToBeatCategory[] | null; - }>({ isLoading: true, data: null }); - - const { userDetails, hasActiveSubscription } = useUserDetails(); - const [activeRequirement, setActiveRequirement] = - useState("minimum"); - - const { gameTitle, shopDetails, objectId, shop, stats, achievements } = - useContext(gameDetailsContext); - - const { showHydraCloudModal } = useSubscription(); - const { t } = useTranslation("game_details"); - const { formatDateTime } = useDate(); - const { numberFormatter } = useFormat(); - - useEffect(() => { - if (objectId) { - setHowLongToBeat({ isLoading: true, data: null }); - - howLongToBeatEntriesTable - .where({ shop, objectId }) - .first() - .then(async (cachedHowLongToBeat) => { - if (cachedHowLongToBeat) { - setHowLongToBeat({ - isLoading: false, - data: cachedHowLongToBeat.categories, - }); - } else { - try { - const howLongToBeat = await window.electron.getHowLongToBeat( - objectId, - shop - ); - - if (howLongToBeat) { - howLongToBeatEntriesTable.add({ - objectId, - shop: "steam", - createdAt: new Date(), - updatedAt: new Date(), - categories: howLongToBeat, - }); - } - - setHowLongToBeat({ isLoading: false, data: howLongToBeat }); - } catch (err) { - setHowLongToBeat({ isLoading: false, data: null }); - } - } - }); - } - }, [objectId, shop, gameTitle]); - - return ( - - ); -} +import { useContext, useEffect, useState } from "react"; +import type { + HowLongToBeatCategory, + SteamAppDetails, + UserAchievement, +} from "@types"; +import { useTranslation } from "react-i18next"; +import { Button, Link } from "@renderer/components"; + +import { gameDetailsContext } from "@renderer/context"; +import { useDate, useFormat, useUserDetails } from "@renderer/hooks"; +import { + CloudOfflineIcon, + DownloadIcon, + LockIcon, + PeopleIcon, +} from "@primer/octicons-react"; +import { HowLongToBeatSection } from "./how-long-to-beat-section"; +import { howLongToBeatEntriesTable } from "@renderer/dexie"; +import { SidebarSection } from "../sidebar-section/sidebar-section"; +import { buildGameAchievementPath } from "@renderer/helpers"; +import { useSubscription } from "@renderer/hooks/use-subscription"; +import "./sidebar.scss"; +import { GamePricesSection } from "./game-prices-section"; +import { GameLanguageSection } from "./game-language-section"; + +const achievementsPlaceholder: UserAchievement[] = [ + { + displayName: "Timber!!", + name: "1", + hidden: false, + description: "Chop down your first tree.", + icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0fbb33098c9da39d1d4771d8209afface9c46e81.jpg", + icongray: + "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0fbb33098c9da39d1d4771d8209afface9c46e81.jpg", + unlocked: true, + unlockTime: Date.now(), + }, + { + displayName: "Supreme Helper Minion!", + name: "2", + hidden: false, + icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0a6ff6a36670c96ceb4d30cf6fd69d2fdf55f38e.jpg", + icongray: + "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/0a6ff6a36670c96ceb4d30cf6fd69d2fdf55f38e.jpg", + unlocked: false, + unlockTime: null, + }, + { + displayName: "Feast of Midas", + name: "3", + hidden: false, + icon: "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/2d10311274fe7c92ab25cc29afdca86b019ad472.jpg", + icongray: + "https://cdn.akamai.steamstatic.com/steamcommunity/public/images/apps/105600/2d10311274fe7c92ab25cc29afdca86b019ad472.jpg", + unlocked: false, + unlockTime: null, + }, +]; + +export function Sidebar() { + const [howLongToBeat, setHowLongToBeat] = useState<{ + isLoading: boolean; + data: HowLongToBeatCategory[] | null; + }>({ isLoading: true, data: null }); + + const { userDetails, hasActiveSubscription } = useUserDetails(); + const [activeRequirement, setActiveRequirement] = + useState("minimum"); + + const { gameTitle, shopDetails, objectId, shop, stats, achievements } = + useContext(gameDetailsContext); + + const { showHydraCloudModal } = useSubscription(); + const { t } = useTranslation("game_details"); + const { formatDateTime } = useDate(); + const { numberFormatter } = useFormat(); + + useEffect(() => { + if (objectId) { + setHowLongToBeat({ isLoading: true, data: null }); + + howLongToBeatEntriesTable + .where({ shop, objectId }) + .first() + .then(async (cachedHowLongToBeat) => { + if (cachedHowLongToBeat) { + setHowLongToBeat({ + isLoading: false, + data: cachedHowLongToBeat.categories, + }); + } else { + try { + const howLongToBeat = await window.electron.getHowLongToBeat( + objectId, + shop + ); + + if (howLongToBeat) { + howLongToBeatEntriesTable.add({ + objectId, + shop: "steam", + createdAt: new Date(), + updatedAt: new Date(), + categories: howLongToBeat, + }); + } + + setHowLongToBeat({ isLoading: false, data: howLongToBeat }); + } catch (err) { + setHowLongToBeat({ isLoading: false, data: null }); + } + } + }); + } + }, [objectId, shop, gameTitle]); + + return ( + + ); +} From 85b40b9a7ea17ecf93a4eab733ec2105baa18452 Mon Sep 17 00:00:00 2001 From: Daniel Saraiva Date: Sun, 7 Sep 2025 12:41:52 -0300 Subject: [PATCH 03/27] added example environment variable --- .env.example | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.env.example b/.env.example index 86a515f6..7138b94c 100644 --- a/.env.example +++ b/.env.example @@ -3,3 +3,5 @@ MAIN_VITE_AUTH_URL=AUTH_URL MAIN_VITE_WS_URL= RENDERER_VITE_REAL_DEBRID_REFERRAL_ID= RENDERER_VITE_TORBOX_REFERRAL_CODE= +VITE_GG_DEALS_API_URL=https://api.gg.deals/v1/prices/by-steam-app-id +VITE_GG_DEALS_API_KEY= \ No newline at end of file From a546b906e9cbe408e1dff01bd36648384ad353f3 Mon Sep 17 00:00:00 2001 From: Daniel Saraiva Date: Wed, 10 Sep 2025 09:04:29 -0300 Subject: [PATCH 04/27] feat: adjustment in hard code text to use translation constant --- src/locales/en/translation.json | 1 + src/locales/pt-BR/translation.json | 1 + .../src/pages/game-details/sidebar/game-prices-section.tsx | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 26b44e58..a941cb01 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -226,6 +226,7 @@ "currency_country": "us", "prices": "Prices", "no_prices_found": "No prices found", + "view_all_prices": "Click to view all prices", "retail_price": "Retail price", "keyshop_price": "Keyshop price", "historical_retail": "Historical retail", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 55729de1..fd6fbd97 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -213,6 +213,7 @@ "currency_country": "br", "prices": "Preços", "no_prices_found": "Nenhum preço encontrado", + "view_all_prices": "Clique para ver todos os preços", "retail_price": "Preço de lojas oficiais", "keyshop_price": "Preço em keyshops", "historical_retail": "Preço histórico de lojas oficiais", diff --git a/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx b/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx index 44560459..0753cdad 100755 --- a/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx @@ -73,7 +73,7 @@ export function GamePricesSection() { rel="noopener noreferrer" className="list__item" > - clique para ver todos os preços + {t("view_all_prices")} From 240b0705d5de8bce49a9d401fabfc8d1061fcb26 Mon Sep 17 00:00:00 2001 From: Wkeynhk <86107421+Wkeynhk@users.noreply.github.com> Date: Thu, 18 Sep 2025 22:06:19 +0300 Subject: [PATCH 05/27] Update translation.json --- src/locales/ru/translation.json | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index e576e5d9..d104a9fc 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -10,7 +10,8 @@ "hot": "Сейчас популярно", "start_typing": "Начинаю вводить текст...", "weekly": "📅 Лучшие игры недели", - "achievements": "🏆 Игры с достижениями" + "achievements": "🏆 Игры с достижениями", + "already_in_library": "Уже в библиотеке" }, "sidebar": { "catalogue": "Каталог", @@ -209,7 +210,22 @@ "invalid_wine_prefix_path": "Недопустимый путь префикса Wine", "invalid_wine_prefix_path_description": "Путь к префиксу Wine недействителен. Пожалуйста, проверьте путь и попробуйте снова.", "missing_wine_prefix": "Префикс Wine необходим для создания резервной копии в Linux", - "download_error_not_cached_on_hydra": "Эта загрузка недоступна на Nimbus." + "download_error_not_cached_on_hydra": "Эта загрузка недоступна на Nimbus.", + "update_playtime_success": "Время игры успешно обновлено", + "update_playtime_error": "Не удалось обновить время игры", + "manual_playtime_warning": "Ваши часы будут отмечены как обновленные вручную, и это нельзя отменить.", + "artifact_renamed": "Резервная копия успешно переименована", + "rename_artifact": "Переименовать резервную копию", + "rename_artifact_description": "Переименуйте резервную копию, присвоив ей более описательное имя.", + "artifact_name_label": "Название резервной копии", + "artifact_name_placeholder": "Введите название для резервной копии", + "max_length_field": "Это поле должно содержать менее {{length}} символов", + "freeze_backup": "Закрепить, чтобы она не была перезаписана автоматическими резервными копиями", + "unfreeze_backup": "Открепить", + "backup_frozen": "Резервная копия закреплена", + "backup_unfrozen": "Резервная копия откреплена", + "backup_freeze_failed": "Не удалось закрепить резервную копию", + "backup_freeze_failed_description": "Вы должны оставить как минимум один свободный слот для автоматических резервных копий" }, "activation": { "title": "Активировать Hydra", @@ -385,7 +401,8 @@ "hidden": "Скрытый", "test_notification": "Тестовое уведомление", "notification_preview": "Предварительный просмотр уведомления о достижении", - "enable_friend_start_game_notifications": "Когда друг начинает играть в игру" + "enable_friend_start_game_notifications": "Когда друг начинает играть в игру", + "enable_steam_achievements": "Включить поиск достижений Steam" }, "notifications": { "download_complete": "Загрузка завершена", @@ -403,7 +420,8 @@ "game_extracted": "{{title}} успешно распакован", "friend_started_playing_game": "{{displayName}} начал играть в игру", "test_achievement_notification_title": "Это тестовое уведомление", - "test_achievement_notification_description": "Довольно круто, да?" + "test_achievement_notification_description": "Довольно круто, да?", + "new_friend_request_description": "{{displayName}} отправил вам запрос в друзья" }, "system_tray": { "open": "Открыть Hydra", @@ -505,7 +523,9 @@ "achievements_unlocked": "Достижения разблокированы", "earned_points": "Заработано очков:", "show_achievements_on_profile": "Покажите свои достижения в профиле", - "show_points_on_profile": "Показывать заработанные очки в своем профиле" + "show_points_on_profile": "Показывать заработанные очки в своем профиле", + "error_adding_friend": "Не удалось отправить запрос в друзья. Пожалуйста, проверьте код друга", + "friend_code_length_error": "Код друга должен содержать 8 символов" }, "achievement": { "achievement_unlocked": "Достижение разблокировано", From c056feb26fd872c674d9a1f19e55e19e08684b92 Mon Sep 17 00:00:00 2001 From: Wkeynhk <86107421+Wkeynhk@users.noreply.github.com> Date: Thu, 18 Sep 2025 22:24:32 +0300 Subject: [PATCH 06/27] Update translation.json --- src/locales/ru/translation.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index d104a9fc..21fb459e 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -225,7 +225,8 @@ "backup_frozen": "Резервная копия закреплена", "backup_unfrozen": "Резервная копия откреплена", "backup_freeze_failed": "Не удалось закрепить резервную копию", - "backup_freeze_failed_description": "Вы должны оставить как минимум один свободный слот для автоматических резервных копий" + "backup_freeze_failed_description": "Вы должны оставить как минимум один свободный слот для автоматических резервных копий", + "manual_playtime_tooltip": "Это время игры было обновлено вручную" }, "activation": { "title": "Активировать Hydra", @@ -414,8 +415,7 @@ "restart_to_install_update": "Перезапустите Hydra для установки обновления", "notification_achievement_unlocked_title": "Достижение разблокировано для {{game}}", "notification_achievement_unlocked_body": "были разблокированы {{achievement}} и другие {{count}}", - "new_friend_request_title": "Новый запрос на добавление в друзья", - "new_friend_request_description": "Вы получили новый запрос на добавление в друзья", + "new_friend_request_title": "Новый запрос на добавление в друзья", "extraction_complete": "Распаковка завершена", "game_extracted": "{{title}} успешно распакован", "friend_started_playing_game": "{{displayName}} начал играть в игру", From b22e082781b197b853a8fd45508dac276b7cdd8a Mon Sep 17 00:00:00 2001 From: Wkeynhk <86107421+Wkeynhk@users.noreply.github.com> Date: Thu, 18 Sep 2025 22:56:30 +0300 Subject: [PATCH 07/27] Update translation.json --- 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 21fb459e..b1e672e1 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -415,7 +415,7 @@ "restart_to_install_update": "Перезапустите Hydra для установки обновления", "notification_achievement_unlocked_title": "Достижение разблокировано для {{game}}", "notification_achievement_unlocked_body": "были разблокированы {{achievement}} и другие {{count}}", - "new_friend_request_title": "Новый запрос на добавление в друзья", + "new_friend_request_title": "Новый запрос на добавление в друзья", "extraction_complete": "Распаковка завершена", "game_extracted": "{{title}} успешно распакован", "friend_started_playing_game": "{{displayName}} начал играть в игру", From a7e4e211673b5f0936ab8af600518fb8880ce008 Mon Sep 17 00:00:00 2001 From: Wkeynhk <86107421+Wkeynhk@users.noreply.github.com> Date: Fri, 19 Sep 2025 08:21:41 +0300 Subject: [PATCH 08/27] Update translation.json --- 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 b1e672e1..b1714804 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -213,7 +213,7 @@ "download_error_not_cached_on_hydra": "Эта загрузка недоступна на Nimbus.", "update_playtime_success": "Время игры успешно обновлено", "update_playtime_error": "Не удалось обновить время игры", - "manual_playtime_warning": "Ваши часы будут отмечены как обновленные вручную, и это нельзя отменить.", + "manual_playtime_warning": "Ваши часы будут отмечены как обновленные вручную. Это действие нельзя отменить.", "artifact_renamed": "Резервная копия успешно переименована", "rename_artifact": "Переименовать резервную копию", "rename_artifact_description": "Переименуйте резервную копию, присвоив ей более описательное имя.", From 33c15baf0e05818cce20f106405f5c0ffd198aab Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 23 Sep 2025 15:21:32 +0300 Subject: [PATCH 09/27] feat: pinning and showing featuring games in profile --- src/locales/en/translation.json | 3 ++ src/main/events/index.ts | 2 ++ src/main/events/library/add-game-to-pinned.ts | 28 +++++++++++++++ .../events/library/remove-game-from-pinned.ts | 28 +++++++++++++++ .../library-sync/merge-with-remote-games.ts | 3 ++ .../library-sync/upload-games-batch.ts | 1 + src/preload/index.ts | 4 +++ src/renderer/src/declaration.d.ts | 5 +++ .../game-details/hero/hero-panel-actions.tsx | 36 +++++++++++++++++++ .../profile-content/profile-content.scss | 28 +++++++++++++++ .../profile-content/profile-content.tsx | 33 +++++++++++++++-- .../user-library-game-card.scss | 14 ++++++++ .../user-library-game-card.tsx | 7 +++- src/types/index.ts | 2 ++ src/types/level.types.ts | 1 + 15 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 src/main/events/library/add-game-to-pinned.ts create mode 100644 src/main/events/library/remove-game-from-pinned.ts diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 9f8de8f8..b5162431 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -210,6 +210,8 @@ "download_error_not_cached_on_hydra": "This download is not available on Nimbus.", "game_removed_from_favorites": "Game removed from favorites", "game_added_to_favorites": "Game added to favorites", + "game_removed_from_pinned": "Game removed from pinned", + "game_added_to_pinned": "Game added to pinned", "automatically_extract_downloaded_files": "Automatically extract downloaded files", "create_start_menu_shortcut": "Create Start Menu shortcut", "invalid_wine_prefix_path": "Invalid Wine prefix path", @@ -451,6 +453,7 @@ "last_time_played": "Last played {{period}}", "activity": "Recent Activity", "library": "Library", + "pinned": "Pinned", "total_play_time": "Total playtime", "manual_playtime_tooltip": "This playtime has been manually updated", "no_recent_activity_title": "Hmmm… nothing here", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 9765b517..733f63d7 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -16,6 +16,8 @@ import "./hardware/check-folder-write-permission"; import "./library/add-game-to-library"; import "./library/add-game-to-favorites"; import "./library/remove-game-from-favorites"; +import "./library/add-game-to-pinned"; +import "./library/remove-game-from-pinned"; import "./library/create-game-shortcut"; import "./library/close-game"; import "./library/delete-game-folder"; diff --git a/src/main/events/library/add-game-to-pinned.ts b/src/main/events/library/add-game-to-pinned.ts new file mode 100644 index 00000000..8ed5921f --- /dev/null +++ b/src/main/events/library/add-game-to-pinned.ts @@ -0,0 +1,28 @@ +import { registerEvent } from "../register-event"; +import { gamesSublevel, levelKeys } from "@main/level"; +import { HydraApi } from "@main/services"; +import type { GameShop } from "@types"; + +const addGameToPinned = async ( + _event: Electron.IpcMainInvokeEvent, + shop: GameShop, + objectId: string +) => { + const gameKey = levelKeys.game(shop, objectId); + + const game = await gamesSublevel.get(gameKey); + if (!game) return; + + HydraApi.put(`/profile/games/${shop}/${objectId}/pin`).catch(() => {}); + + try { + await gamesSublevel.put(gameKey, { + ...game, + pinned: true, + }); + } catch (error) { + throw new Error(`Failed to update game pinned status: ${error}`); + } +}; + +registerEvent("addGameToPinned", addGameToPinned); \ No newline at end of file diff --git a/src/main/events/library/remove-game-from-pinned.ts b/src/main/events/library/remove-game-from-pinned.ts new file mode 100644 index 00000000..613284bd --- /dev/null +++ b/src/main/events/library/remove-game-from-pinned.ts @@ -0,0 +1,28 @@ +import { registerEvent } from "../register-event"; +import { gamesSublevel, levelKeys } from "@main/level"; +import { HydraApi } from "@main/services"; +import type { GameShop } from "@types"; + +const removeGameFromPinned = async ( + _event: Electron.IpcMainInvokeEvent, + shop: GameShop, + objectId: string +) => { + const gameKey = levelKeys.game(shop, objectId); + + const game = await gamesSublevel.get(gameKey); + if (!game) return; + + HydraApi.put(`/profile/games/${shop}/${objectId}/unpin`).catch(() => {}); + + try { + await gamesSublevel.put(gameKey, { + ...game, + pinned: false, + }); + } catch (error) { + throw new Error(`Failed to update game pinned status: ${error}`); + } +}; + +registerEvent("removeGameFromPinned", removeGameFromPinned); \ No newline at end of file diff --git a/src/main/services/library-sync/merge-with-remote-games.ts b/src/main/services/library-sync/merge-with-remote-games.ts index a35414ac..0d5d92f8 100644 --- a/src/main/services/library-sync/merge-with-remote-games.ts +++ b/src/main/services/library-sync/merge-with-remote-games.ts @@ -8,6 +8,7 @@ type ProfileGame = { playTimeInMilliseconds: number; hasManuallyUpdatedPlaytime: boolean; isFavorite?: boolean; + isPinned?: boolean; } & ShopAssets; export const mergeWithRemoteGames = async () => { @@ -36,6 +37,7 @@ export const mergeWithRemoteGames = async () => { lastTimePlayed: updatedLastTimePlayed, playTimeInMilliseconds: updatedPlayTime, favorite: game.isFavorite ?? localGame.favorite, + pinned: game.isPinned ?? localGame.pinned, }); } else { await gamesSublevel.put(gameKey, { @@ -49,6 +51,7 @@ export const mergeWithRemoteGames = async () => { hasManuallyUpdatedPlaytime: game.hasManuallyUpdatedPlaytime, isDeleted: false, favorite: game.isFavorite ?? false, + pinned: game.isPinned ?? false, }); } diff --git a/src/main/services/library-sync/upload-games-batch.ts b/src/main/services/library-sync/upload-games-batch.ts index 837fb48a..beab164f 100644 --- a/src/main/services/library-sync/upload-games-batch.ts +++ b/src/main/services/library-sync/upload-games-batch.ts @@ -27,6 +27,7 @@ export const uploadGamesBatch = async () => { shop: game.shop, lastTimePlayed: game.lastTimePlayed, isFavorite: game.favorite, + isPinned: game.pinned ?? false, }; }) ).catch(() => {}); diff --git a/src/preload/index.ts b/src/preload/index.ts index d29417b0..b3c4f400 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -143,6 +143,10 @@ contextBridge.exposeInMainWorld("electron", { ipcRenderer.invoke("addGameToFavorites", shop, objectId), removeGameFromFavorites: (shop: GameShop, objectId: string) => ipcRenderer.invoke("removeGameFromFavorites", shop, objectId), + addGameToPinned: (shop: GameShop, objectId: string) => + ipcRenderer.invoke("addGameToPinned", shop, objectId), + removeGameFromPinned: (shop: GameShop, objectId: string) => + ipcRenderer.invoke("removeGameFromPinned", shop, objectId), updateLaunchOptions: ( shop: GameShop, objectId: string, diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 0744884c..434c0adb 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -126,6 +126,11 @@ declare global { shop: GameShop, objectId: string ) => Promise; + addGameToPinned: (shop: GameShop, objectId: string) => Promise; + removeGameFromPinned: ( + shop: GameShop, + objectId: string + ) => Promise; updateLaunchOptions: ( shop: GameShop, objectId: string, diff --git a/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx b/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx index a3b75d2e..bb3dfe98 100644 --- a/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx +++ b/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx @@ -3,6 +3,8 @@ import { GearIcon, HeartFillIcon, HeartIcon, + PinIcon, + PinSlashIcon, PlayIcon, PlusCircleIcon, } from "@primer/octicons-react"; @@ -82,6 +84,31 @@ export function HeroPanelActions() { } }; + const toggleGamePinned = async () => { + setToggleLibraryGameDisabled(true); + + try { + if (game?.pinned && objectId) { + await window.electron + .removeGameFromPinned(shop, objectId) + .then(() => { + showSuccessToast(t("game_removed_from_pinned")); + }); + } else { + if (!objectId) return; + + await window.electron.addGameToPinned(shop, objectId).then(() => { + showSuccessToast(t("game_added_to_pinned")); + }); + } + + updateLibrary(); + updateGame(); + } finally { + setToggleLibraryGameDisabled(false); + } + }; + const openGame = async () => { if (game) { if (game.executablePath) { @@ -198,6 +225,15 @@ export function HeroPanelActions() { {game.favorite ? : } + + - + {userDetails && ( + + )} - {userDetails && ( + {userDetails && shop !== "custom" && ( - {userDetails && shop !== "custom" && ( + {userDetails && ( + )} )} Date: Thu, 25 Sep 2025 20:23:33 +0300 Subject: [PATCH 19/27] feat: added pin/unpin to the game card in profile --- .../user-library-game-card.tsx | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index ca31b5de..8436b5d6 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -35,7 +35,8 @@ export function UserLibraryGameCard({ onMouseEnter, onMouseLeave, }: UserLibraryGameCardProps) { - const { userProfile, isMe, getUserLibraryGames } = useContext(userProfileContext); + const { userProfile, isMe, getUserLibraryGames } = + useContext(userProfileContext); const { t } = useTranslation("user_profile"); const { t: tGame } = useTranslation("game_details"); const { numberFormatter } = useFormat(); @@ -99,18 +100,22 @@ export function UserLibraryGameCard({ try { if (game.isPinned) { - await window.electron.removeGameFromPinned(game.shop, game.objectId).then(() => { - showSuccessToast(tGame("game_removed_from_pinned")); - }); + await window.electron + .removeGameFromPinned(game.shop, game.objectId) + .then(() => { + showSuccessToast(tGame("game_removed_from_pinned")); + }); } else { - await window.electron.addGameToPinned(game.shop, game.objectId).then(() => { - showSuccessToast(tGame("game_added_to_pinned")); - }); + await window.electron + .addGameToPinned(game.shop, game.objectId) + .then(() => { + showSuccessToast(tGame("game_added_to_pinned")); + }); } - + // Add a small delay to allow server synchronization before refreshing - await new Promise(resolve => setTimeout(resolve, 1000)); - + await new Promise((resolve) => setTimeout(resolve, 1000)); + // Refresh the library games to update the UI await getUserLibraryGames(); } finally { @@ -149,7 +154,11 @@ export function UserLibraryGameCard({ }} disabled={isPinning} > - {game.isPinned ? : } + {game.isPinned ? ( + + ) : ( + + )} )} From fd1f13225b6ba7006a8ec765d12f80fe04a4dc97 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Thu, 25 Sep 2025 20:26:49 +0300 Subject: [PATCH 20/27] feat: added pin/unpin to the game card in profile --- .../user-library-game-card.tsx | 33 +++++++------------ 1 file changed, 11 insertions(+), 22 deletions(-) diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index 8436b5d6..e3a5911e 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -35,8 +35,7 @@ export function UserLibraryGameCard({ onMouseEnter, onMouseLeave, }: UserLibraryGameCardProps) { - const { userProfile, isMe, getUserLibraryGames } = - useContext(userProfileContext); + const { userProfile, isMe, getUserLibraryGames } = useContext(userProfileContext); const { t } = useTranslation("user_profile"); const { t: tGame } = useTranslation("game_details"); const { numberFormatter } = useFormat(); @@ -100,23 +99,17 @@ export function UserLibraryGameCard({ try { if (game.isPinned) { - await window.electron - .removeGameFromPinned(game.shop, game.objectId) - .then(() => { - showSuccessToast(tGame("game_removed_from_pinned")); - }); + await window.electron.removeGameFromPinned(game.shop, game.objectId).then(() => { + showSuccessToast(tGame("game_removed_from_pinned")); + }); } else { - await window.electron - .addGameToPinned(game.shop, game.objectId) - .then(() => { - showSuccessToast(tGame("game_added_to_pinned")); - }); + await window.electron.addGameToPinned(game.shop, game.objectId).then(() => { + showSuccessToast(tGame("game_added_to_pinned")); + }); } - - // Add a small delay to allow server synchronization before refreshing - await new Promise((resolve) => setTimeout(resolve, 1000)); - - // Refresh the library games to update the UI + + await new Promise(resolve => setTimeout(resolve, 1000)); + await getUserLibraryGames(); } finally { setIsPinning(false); @@ -154,11 +147,7 @@ export function UserLibraryGameCard({ }} disabled={isPinning} > - {game.isPinned ? ( - - ) : ( - - )} + {game.isPinned ? : } )} From f027f05e0214e9ab2f5b7b5f640e702d07da541b Mon Sep 17 00:00:00 2001 From: Moyasee Date: Fri, 26 Sep 2025 16:54:10 +0300 Subject: [PATCH 21/27] feat: added functionality to collapse/expand pinned list in user profile --- .../src/hooks/use-section-collapse.ts | 30 +++ .../profile-content/profile-content.scss | 34 +++- .../profile-content/profile-content.tsx | 172 +++++++++++++++--- .../user-library-game-card.tsx | 31 ++-- 4 files changed, 229 insertions(+), 38 deletions(-) create mode 100644 src/renderer/src/hooks/use-section-collapse.ts diff --git a/src/renderer/src/hooks/use-section-collapse.ts b/src/renderer/src/hooks/use-section-collapse.ts new file mode 100644 index 00000000..3c534189 --- /dev/null +++ b/src/renderer/src/hooks/use-section-collapse.ts @@ -0,0 +1,30 @@ +import { useState, useCallback } from "react"; + +interface SectionCollapseState { + pinned: boolean; + library: boolean; +} + +export function useSectionCollapse() { + const [collapseState, setCollapseState] = useState({ + pinned: false, + library: false, + }); + + const toggleSection = useCallback( + (section: keyof SectionCollapseState) => { + setCollapseState(prevState => ({ + ...prevState, + [section]: !prevState[section], + })); + }, + [] + ); + + return { + collapseState, + toggleSection, + isPinnedCollapsed: collapseState.pinned, + isLibraryCollapsed: collapseState.library, + }; +} \ No newline at end of file diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.scss b/src/renderer/src/pages/profile/profile-content/profile-content.scss index 57a24e2f..4ef53a6d 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.scss +++ b/src/renderer/src/pages/profile/profile-content/profile-content.scss @@ -54,8 +54,40 @@ &__section-header { display: flex; align-items: center; - justify-content: space-between; margin-bottom: calc(globals.$spacing-unit * 2); + gap: calc(globals.$spacing-unit); + } + + &__section-title-group { + display: flex; + align-items: center; + gap: calc(globals.$spacing-unit); + flex: 1; + } + + &__section-count { + margin-left: auto; + } + + &__collapse-button { + background: none; + border: none; + color: rgba(255, 255, 255, 0.7); + cursor: pointer; + padding: 4px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all ease 0.2s; + flex-shrink: 0; + + &:hover { + color: rgba(255, 255, 255, 0.9); + background-color: rgba(255, 255, 255, 0.1); + } + + } &__tabs { diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.tsx b/src/renderer/src/pages/profile/profile-content/profile-content.tsx index 3c27ae02..bd7eddb3 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -3,7 +3,7 @@ import { useContext, useEffect, useMemo, useRef, useState } from "react"; import { ProfileHero } from "../profile-hero/profile-hero"; import { useAppDispatch, useFormat } from "@renderer/hooks"; import { setHeaderTitle } from "@renderer/features"; -import { TelescopeIcon } from "@primer/octicons-react"; +import { TelescopeIcon, ChevronRightIcon } from "@primer/octicons-react"; import { useTranslation } from "react-i18next"; import { LockedProfile } from "./locked-profile"; import { ReportProfile } from "../report-profile/report-profile"; @@ -11,16 +11,80 @@ import { FriendsBox } from "./friends-box"; import { RecentGamesBox } from "./recent-games-box"; import { UserStatsBox } from "./user-stats-box"; import { UserLibraryGameCard } from "./user-library-game-card"; +import { useSectionCollapse } from "@renderer/hooks/use-section-collapse"; +import { motion, AnimatePresence } from "framer-motion"; import "./profile-content.scss"; const GAME_STATS_ANIMATION_DURATION_IN_MS = 3500; +const sectionVariants = { + collapsed: { + opacity: 0, + y: -20, + height: 0, + transition: { + duration: 0.3, + ease: [0.25, 0.1, 0.25, 1], + opacity: { duration: 0.1 }, + y: { duration: 0.1 }, + height: { duration: 0.2 } + } + }, + expanded: { + opacity: 1, + y: 0, + height: "auto", + transition: { + duration: 0.3, + ease: [0.25, 0.1, 0.25, 1], + opacity: { duration: 0.2, delay: 0.1 }, + y: { duration: 0.3 }, + height: { duration: 0.3 } + } + } +}; + +const gameCardVariants = { + hidden: { + opacity: 0, + y: 20, + scale: 0.95 + }, + visible: { + opacity: 1, + y: 0, + scale: 1, + transition: { + duration: 0.4, + ease: [0.25, 0.1, 0.25, 1] + } + } +}; + +const chevronVariants = { + collapsed: { + rotate: 0, + transition: { + duration: 0.2, + ease: "easeInOut" + } + }, + expanded: { + rotate: 90, + transition: { + duration: 0.2, + ease: "easeInOut" + } + } +}; + export function ProfileContent() { const { userProfile, isMe, userStats, libraryGames, pinnedGames } = useContext(userProfileContext); const [statsIndex, setStatsIndex] = useState(0); const [isAnimationRunning, setIsAnimationRunning] = useState(true); const statsAnimation = useRef(-1); + const { toggleSection, isPinnedCollapsed } = useSectionCollapse(); const dispatch = useAppDispatch(); @@ -101,53 +165,107 @@ export function ProfileContent() { )} {hasAnyGames && ( - <> +
{hasPinnedGames && ( -
+
-

{t("pinned")}

- {pinnedGames.length} +
+ +

{t("pinned")}

+
+ + {pinnedGames.length} +
-
    - {pinnedGames?.map((game) => ( - - ))} -
+ + {!isPinnedCollapsed && ( + +
    + {pinnedGames?.map((game, index) => ( + + + + ))} +
+
+ )} +
)} {hasGames && (
-

{t("library")}

+
+

{t("library")}

+
{userStats && ( - + {numberFormatter.format(userStats.libraryCount)} )}
    - {libraryGames?.map((game) => ( - ( + + variants={gameCardVariants} + initial="hidden" + animate="visible" + transition={{ delay: index * 0.1 }} + style={{ listStyle: 'none' }} + > + + ))}
)} - +
)}
@@ -171,6 +289,8 @@ export function ProfileContent() { statsIndex, libraryGames, pinnedGames, + isPinnedCollapsed, + toggleSection, ]); return ( diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index e3a5911e..e00b5863 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -35,7 +35,8 @@ export function UserLibraryGameCard({ onMouseEnter, onMouseLeave, }: UserLibraryGameCardProps) { - const { userProfile, isMe, getUserLibraryGames } = useContext(userProfileContext); + const { userProfile, isMe, getUserLibraryGames } = + useContext(userProfileContext); const { t } = useTranslation("user_profile"); const { t: tGame } = useTranslation("game_details"); const { numberFormatter } = useFormat(); @@ -99,17 +100,21 @@ export function UserLibraryGameCard({ try { if (game.isPinned) { - await window.electron.removeGameFromPinned(game.shop, game.objectId).then(() => { - showSuccessToast(tGame("game_removed_from_pinned")); - }); + await window.electron + .removeGameFromPinned(game.shop, game.objectId) + .then(() => { + showSuccessToast(tGame("game_removed_from_pinned")); + }); } else { - await window.electron.addGameToPinned(game.shop, game.objectId).then(() => { - showSuccessToast(tGame("game_added_to_pinned")); - }); + await window.electron + .addGameToPinned(game.shop, game.objectId) + .then(() => { + showSuccessToast(tGame("game_added_to_pinned")); + }); } - - await new Promise(resolve => setTimeout(resolve, 1000)); - + + await new Promise((resolve) => setTimeout(resolve, 1000)); + await getUserLibraryGames(); } finally { setIsPinning(false); @@ -147,7 +152,11 @@ export function UserLibraryGameCard({ }} disabled={isPinning} > - {game.isPinned ? : } + {game.isPinned ? ( + + ) : ( + + )} )} From b6be03cea364b30bc19fb717d8d0bcd77ecbbd9a Mon Sep 17 00:00:00 2001 From: Moyasee Date: Fri, 26 Sep 2025 17:00:50 +0300 Subject: [PATCH 22/27] fix: formatting issues --- .../src/hooks/use-section-collapse.ts | 17 +++---- .../profile-content/profile-content.scss | 4 +- .../profile-content/profile-content.tsx | 50 +++++++++---------- 3 files changed, 32 insertions(+), 39 deletions(-) diff --git a/src/renderer/src/hooks/use-section-collapse.ts b/src/renderer/src/hooks/use-section-collapse.ts index 3c534189..7cd22224 100644 --- a/src/renderer/src/hooks/use-section-collapse.ts +++ b/src/renderer/src/hooks/use-section-collapse.ts @@ -11,15 +11,12 @@ export function useSectionCollapse() { library: false, }); - const toggleSection = useCallback( - (section: keyof SectionCollapseState) => { - setCollapseState(prevState => ({ - ...prevState, - [section]: !prevState[section], - })); - }, - [] - ); + const toggleSection = useCallback((section: keyof SectionCollapseState) => { + setCollapseState((prevState) => ({ + ...prevState, + [section]: !prevState[section], + })); + }, []); return { collapseState, @@ -27,4 +24,4 @@ export function useSectionCollapse() { isPinnedCollapsed: collapseState.pinned, isLibraryCollapsed: collapseState.library, }; -} \ No newline at end of file +} diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.scss b/src/renderer/src/pages/profile/profile-content/profile-content.scss index 4ef53a6d..8f2fcf6f 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.scss +++ b/src/renderer/src/pages/profile/profile-content/profile-content.scss @@ -81,13 +81,11 @@ justify-content: center; transition: all ease 0.2s; flex-shrink: 0; - + &:hover { color: rgba(255, 255, 255, 0.9); background-color: rgba(255, 255, 255, 0.1); } - - } &__tabs { diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.tsx b/src/renderer/src/pages/profile/profile-content/profile-content.tsx index bd7eddb3..f1bb2ab8 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -27,8 +27,8 @@ const sectionVariants = { ease: [0.25, 0.1, 0.25, 1], opacity: { duration: 0.1 }, y: { duration: 0.1 }, - height: { duration: 0.2 } - } + height: { duration: 0.2 }, + }, }, expanded: { opacity: 1, @@ -39,16 +39,16 @@ const sectionVariants = { ease: [0.25, 0.1, 0.25, 1], opacity: { duration: 0.2, delay: 0.1 }, y: { duration: 0.3 }, - height: { duration: 0.3 } - } - } + height: { duration: 0.3 }, + }, + }, }; const gameCardVariants = { hidden: { opacity: 0, y: 20, - scale: 0.95 + scale: 0.95, }, visible: { opacity: 1, @@ -56,9 +56,9 @@ const gameCardVariants = { scale: 1, transition: { duration: 0.4, - ease: [0.25, 0.1, 0.25, 1] - } - } + ease: [0.25, 0.1, 0.25, 1], + }, + }, }; const chevronVariants = { @@ -66,16 +66,16 @@ const chevronVariants = { rotate: 0, transition: { duration: 0.2, - ease: "easeInOut" - } + ease: "easeInOut", + }, }, expanded: { rotate: 90, transition: { duration: 0.2, - ease: "easeInOut" - } - } + ease: "easeInOut", + }, + }, }; export function ProfileContent() { @@ -167,16 +167,18 @@ export function ProfileContent() { {hasAnyGames && (
{hasPinnedGames && ( -
+
- + {pinnedGames.length}
@@ -212,7 +212,7 @@ export function ProfileContent() { initial="hidden" animate="visible" transition={{ delay: index * 0.1 }} - style={{ listStyle: 'none' }} + style={{ listStyle: "none" }} > {t("library")}
{userStats && ( - + {numberFormatter.format(userStats.libraryCount)} )} @@ -252,7 +250,7 @@ export function ProfileContent() { initial="hidden" animate="visible" transition={{ delay: index * 0.1 }} - style={{ listStyle: 'none' }} + style={{ listStyle: "none" }} > Date: Sun, 28 Sep 2025 00:37:22 +0100 Subject: [PATCH 23/27] feat: adding profile sorting --- src/locales/en/translation.json | 7 +- src/main/events/index.ts | 3 +- src/main/events/library/add-game-to-pinned.ts | 29 -- .../events/library/remove-game-from-pinned.ts | 29 -- src/main/events/library/toggle-game-pin.ts | 43 +++ src/main/events/user/get-user-library.ts | 7 +- .../library-sync/merge-with-remote-games.ts | 4 +- .../library-sync/upload-games-batch.ts | 2 +- src/preload/index.ts | 14 +- .../user-profile/user-profile.context.tsx | 37 ++- src/renderer/src/declaration.d.ts | 10 +- .../game-details/hero/hero-panel-actions.tsx | 8 +- .../modals/change-game-playtime-modal.tsx | 1 - .../profile-content/profile-content.scss | 99 ++++++- .../profile-content/profile-content.tsx | 276 ++++++++++++++---- .../user-library-game-card.tsx | 27 +- src/types/level.types.ts | 2 +- 17 files changed, 437 insertions(+), 161 deletions(-) delete mode 100644 src/main/events/library/add-game-to-pinned.ts delete mode 100644 src/main/events/library/remove-game-from-pinned.ts create mode 100644 src/main/events/library/toggle-game-pin.ts diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index b5162431..14241cdf 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -454,6 +454,9 @@ "activity": "Recent Activity", "library": "Library", "pinned": "Pinned", + "achievements_earned": "Achievements earned", + "played_recently": "Played recently", + "playtime": "Playtime", "total_play_time": "Total playtime", "manual_playtime_tooltip": "This playtime has been manually updated", "no_recent_activity_title": "Hmmm… nothing here", @@ -530,7 +533,9 @@ "show_achievements_on_profile": "Show your achievements on your profile", "show_points_on_profile": "Show your earned points on your profile", "error_adding_friend": "Could not send friend request. Please check friend code", - "friend_code_length_error": "Friend code must have 8 characters" + "friend_code_length_error": "Friend code must have 8 characters", + "game_removed_from_pinned": "Game removed from pinned", + "game_added_to_pinned": "Game added to pinned" }, "achievement": { "achievement_unlocked": "Achievement unlocked", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 00b387d2..6bd74b69 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -16,8 +16,7 @@ import "./hardware/check-folder-write-permission"; import "./library/add-game-to-library"; import "./library/add-game-to-favorites"; import "./library/remove-game-from-favorites"; -import "./library/add-game-to-pinned"; -import "./library/remove-game-from-pinned"; +import "./library/toggle-game-pin"; import "./library/create-game-shortcut"; import "./library/close-game"; import "./library/delete-game-folder"; diff --git a/src/main/events/library/add-game-to-pinned.ts b/src/main/events/library/add-game-to-pinned.ts deleted file mode 100644 index 82b62d7b..00000000 --- a/src/main/events/library/add-game-to-pinned.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { registerEvent } from "../register-event"; -import { gamesSublevel, levelKeys } from "@main/level"; -import { HydraApi } from "@main/services"; -import type { GameShop } from "@types"; - -const addGameToPinned = async ( - _event: Electron.IpcMainInvokeEvent, - shop: GameShop, - objectId: string -) => { - const gameKey = levelKeys.game(shop, objectId); - - const game = await gamesSublevel.get(gameKey); - if (!game) return; - - const response = await HydraApi.put(`/profile/games/${shop}/${objectId}/pin`); - - try { - await gamesSublevel.put(gameKey, { - ...game, - pinned: true, - pinnedDate: new Date(response.pinnedDate), - }); - } catch (error) { - throw new Error(`Failed to update game pinned status: ${error}`); - } -}; - -registerEvent("addGameToPinned", addGameToPinned); diff --git a/src/main/events/library/remove-game-from-pinned.ts b/src/main/events/library/remove-game-from-pinned.ts deleted file mode 100644 index 658b9d6d..00000000 --- a/src/main/events/library/remove-game-from-pinned.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { registerEvent } from "../register-event"; -import { gamesSublevel, levelKeys } from "@main/level"; -import { HydraApi } from "@main/services"; -import type { GameShop } from "@types"; - -const removeGameFromPinned = async ( - _event: Electron.IpcMainInvokeEvent, - shop: GameShop, - objectId: string -) => { - const gameKey = levelKeys.game(shop, objectId); - - const game = await gamesSublevel.get(gameKey); - if (!game) return; - - HydraApi.put(`/profile/games/${shop}/${objectId}/unpin`).catch(() => {}); - - try { - await gamesSublevel.put(gameKey, { - ...game, - pinned: false, - pinnedDate: null, - }); - } catch (error) { - throw new Error(`Failed to update game pinned status: ${error}`); - } -}; - -registerEvent("removeGameFromPinned", removeGameFromPinned); diff --git a/src/main/events/library/toggle-game-pin.ts b/src/main/events/library/toggle-game-pin.ts new file mode 100644 index 00000000..addedddd --- /dev/null +++ b/src/main/events/library/toggle-game-pin.ts @@ -0,0 +1,43 @@ +import { registerEvent } from "../register-event"; +import { gamesSublevel, levelKeys } from "@main/level"; +import { HydraApi, logger } from "@main/services"; +import type { GameShop, UserGame } from "@types"; + +const toggleGamePin = async ( + _event: Electron.IpcMainInvokeEvent, + shop: GameShop, + objectId: string, + pin: boolean +) => { + try { + const gameKey = levelKeys.game(shop, objectId); + + const game = await gamesSublevel.get(gameKey); + if (!game) return; + + if (pin) { + const response = await HydraApi.put( + `/profile/games/${shop}/${objectId}/pin` + ); + + await gamesSublevel.put(gameKey, { + ...game, + isPinned: pin, + pinnedDate: new Date(response.pinnedDate!), + }); + } else { + await HydraApi.put(`/profile/games/${shop}/${objectId}/unpin`); + + await gamesSublevel.put(gameKey, { + ...game, + isPinned: pin, + pinnedDate: null, + }); + } + } catch (error) { + logger.error("Failed to update game pinned status", error); + throw new Error(`Failed to update game pinned status: ${error}`); + } +}; + +registerEvent("toggleGamePin", toggleGamePin); diff --git a/src/main/events/user/get-user-library.ts b/src/main/events/user/get-user-library.ts index 8a715a49..f3c3eed5 100644 --- a/src/main/events/user/get-user-library.ts +++ b/src/main/events/user/get-user-library.ts @@ -6,13 +6,18 @@ const getUserLibrary = async ( _event: Electron.IpcMainInvokeEvent, userId: string, take: number = 12, - skip: number = 0 + skip: number = 0, + sortBy?: string ): Promise => { const params = new URLSearchParams(); params.append("take", take.toString()); params.append("skip", skip.toString()); + if (sortBy) { + params.append("sortBy", sortBy); + } + const queryString = params.toString(); const baseUrl = `/users/${userId}/library`; const url = queryString ? `${baseUrl}?${queryString}` : baseUrl; diff --git a/src/main/services/library-sync/merge-with-remote-games.ts b/src/main/services/library-sync/merge-with-remote-games.ts index 0d5d92f8..152e1138 100644 --- a/src/main/services/library-sync/merge-with-remote-games.ts +++ b/src/main/services/library-sync/merge-with-remote-games.ts @@ -37,7 +37,7 @@ export const mergeWithRemoteGames = async () => { lastTimePlayed: updatedLastTimePlayed, playTimeInMilliseconds: updatedPlayTime, favorite: game.isFavorite ?? localGame.favorite, - pinned: game.isPinned ?? localGame.pinned, + isPinned: game.isPinned ?? localGame.isPinned, }); } else { await gamesSublevel.put(gameKey, { @@ -51,7 +51,7 @@ export const mergeWithRemoteGames = async () => { hasManuallyUpdatedPlaytime: game.hasManuallyUpdatedPlaytime, isDeleted: false, favorite: game.isFavorite ?? false, - pinned: game.isPinned ?? false, + isPinned: game.isPinned ?? false, }); } diff --git a/src/main/services/library-sync/upload-games-batch.ts b/src/main/services/library-sync/upload-games-batch.ts index beab164f..d4febfea 100644 --- a/src/main/services/library-sync/upload-games-batch.ts +++ b/src/main/services/library-sync/upload-games-batch.ts @@ -27,7 +27,7 @@ export const uploadGamesBatch = async () => { shop: game.shop, lastTimePlayed: game.lastTimePlayed, isFavorite: game.favorite, - isPinned: game.pinned ?? false, + isPinned: game.isPinned ?? false, }; }) ).catch(() => {}); diff --git a/src/preload/index.ts b/src/preload/index.ts index b32fd6b0..ca275c91 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -143,10 +143,8 @@ contextBridge.exposeInMainWorld("electron", { ipcRenderer.invoke("addGameToFavorites", shop, objectId), removeGameFromFavorites: (shop: GameShop, objectId: string) => ipcRenderer.invoke("removeGameFromFavorites", shop, objectId), - addGameToPinned: (shop: GameShop, objectId: string) => - ipcRenderer.invoke("addGameToPinned", shop, objectId), - removeGameFromPinned: (shop: GameShop, objectId: string) => - ipcRenderer.invoke("removeGameFromPinned", shop, objectId), + toggleGamePin: (shop: GameShop, objectId: string, pinned: boolean) => + ipcRenderer.invoke("toggleGamePin", shop, objectId, pinned), updateLaunchOptions: ( shop: GameShop, objectId: string, @@ -370,8 +368,12 @@ contextBridge.exposeInMainWorld("electron", { /* User */ getUser: (userId: string) => ipcRenderer.invoke("getUser", userId), - getUserLibrary: (userId: string, take?: number, skip?: number) => - ipcRenderer.invoke("getUserLibrary", userId, take, skip), + getUserLibrary: ( + userId: string, + take?: number, + skip?: number, + sortBy?: string + ) => ipcRenderer.invoke("getUserLibrary", userId, take, skip, sortBy), blockUser: (userId: string) => ipcRenderer.invoke("blockUser", userId), unblockUser: (userId: string) => ipcRenderer.invoke("unblockUser", userId), getUserFriends: (userId: string, take: number, skip: number) => diff --git a/src/renderer/src/context/user-profile/user-profile.context.tsx b/src/renderer/src/context/user-profile/user-profile.context.tsx index 499b85ee..2750442a 100644 --- a/src/renderer/src/context/user-profile/user-profile.context.tsx +++ b/src/renderer/src/context/user-profile/user-profile.context.tsx @@ -14,7 +14,7 @@ export interface UserProfileContext { isMe: boolean; userStats: UserStats | null; getUserProfile: () => Promise; - getUserLibraryGames: () => Promise; + getUserLibraryGames: (sortBy?: string) => Promise; setSelectedBackgroundImage: React.Dispatch>; backgroundImage: string; badges: Badge[]; @@ -30,7 +30,7 @@ export const userProfileContext = createContext({ isMe: false, userStats: null, getUserProfile: async () => {}, - getUserLibraryGames: async () => {}, + getUserLibraryGames: async (_sortBy?: string) => {}, setSelectedBackgroundImage: () => {}, backgroundImage: "", badges: [], @@ -93,21 +93,30 @@ export function UserProfileContextProvider({ }); }, [userId]); - const getUserLibraryGames = useCallback(async () => { - try { - const response = await window.electron.getUserLibrary(userId); - if (response) { - setLibraryGames(response.library); - setPinnedGames(response.pinnedGames); - } else { + const getUserLibraryGames = useCallback( + async (sortBy?: string) => { + try { + const response = await window.electron.getUserLibrary( + userId, + 12, + 0, + sortBy + ); + + if (response) { + setLibraryGames(response.library); + setPinnedGames(response.pinnedGames); + } else { + setLibraryGames([]); + setPinnedGames([]); + } + } catch (error) { setLibraryGames([]); setPinnedGames([]); } - } catch (error) { - setLibraryGames([]); - setPinnedGames([]); - } - }, [userId]); + }, + [userId] + ); const getUserProfile = useCallback(async () => { getUserStats(); diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 115841d4..87b2d63d 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -127,8 +127,11 @@ declare global { shop: GameShop, objectId: string ) => Promise; - addGameToPinned: (shop: GameShop, objectId: string) => Promise; - removeGameFromPinned: (shop: GameShop, objectId: string) => Promise; + toggleGamePin: ( + shop: GameShop, + objectId: string, + pinned: boolean + ) => Promise; updateLaunchOptions: ( shop: GameShop, objectId: string, @@ -293,7 +296,8 @@ declare global { getUserLibrary: ( userId: string, take?: number, - skip?: number + skip?: number, + sortBy?: string ) => Promise; blockUser: (userId: string) => Promise; unblockUser: (userId: string) => Promise; diff --git a/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx b/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx index bdc8cf83..307de108 100644 --- a/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx +++ b/src/renderer/src/pages/game-details/hero/hero-panel-actions.tsx @@ -94,14 +94,14 @@ export function HeroPanelActions() { setToggleLibraryGameDisabled(true); try { - if (game?.pinned && objectId) { - await window.electron.removeGameFromPinned(shop, objectId).then(() => { + if (game?.isPinned && objectId) { + await window.electron.toggleGamePin(shop, objectId, false).then(() => { showSuccessToast(t("game_removed_from_pinned")); }); } else { if (!objectId) return; - await window.electron.addGameToPinned(shop, objectId).then(() => { + await window.electron.toggleGamePin(shop, objectId, true).then(() => { showSuccessToast(t("game_added_to_pinned")); }); } @@ -236,7 +236,7 @@ export function HeroPanelActions() { disabled={deleting} className="hero-panel-actions__action" > - {game.pinned ? : } + {game.isPinned ? : } )} diff --git a/src/renderer/src/pages/game-details/modals/change-game-playtime-modal.tsx b/src/renderer/src/pages/game-details/modals/change-game-playtime-modal.tsx index c9d26b94..7355461a 100644 --- a/src/renderer/src/pages/game-details/modals/change-game-playtime-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/change-game-playtime-modal.tsx @@ -72,7 +72,6 @@ export function ChangeGamePlaytimeModal({ onSuccess?.(t("update_playtime_success")); onClose(); } catch (error) { - console.log(error); onError?.(t("update_playtime_error")); } finally { setIsSubmitting(false); diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.scss b/src/renderer/src/pages/profile/profile-content/profile-content.scss index 8f2fcf6f..7faae2db 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.scss +++ b/src/renderer/src/pages/profile/profile-content/profile-content.scss @@ -65,8 +65,103 @@ flex: 1; } - &__section-count { - margin-left: auto; + &__section-badge { + background-color: rgba(255, 255, 255, 0.1); + color: rgba(255, 255, 255, 0.7); + padding: 4px 8px; + border-radius: 6px; + font-size: 12px; + font-weight: 600; + min-width: 24px; + text-align: center; + flex-shrink: 0; + } + + &__sort-container { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: calc(globals.$spacing-unit); + margin-bottom: calc(globals.$spacing-unit * 2); + } + + &__sort-label { + color: rgba(255, 255, 255, 0.6); + font-size: 14px; + font-weight: 400; + } + + &__sort-options { + display: flex; + align-items: center; + gap: calc(globals.$spacing-unit); + font-size: 14px; + } + + &__sort-option { + background: none; + border: none; + color: rgba(255, 255, 255, 0.4); + cursor: pointer; + padding: 4px 0; + font-size: 14px; + font-weight: 300; + transition: all ease 0.2s; + display: flex; + align-items: center; + gap: 6px; + + &:hover:not(:disabled) { + color: rgba(255, 255, 255, 0.6); + } + + &.active { + color: rgba(255, 255, 255, 0.9); + font-weight: 500; + } + + &.loading { + color: rgba(201, 170, 113, 0.8); + font-weight: 500; + position: relative; + + &::after { + content: ""; + position: absolute; + right: -20px; + top: 50%; + transform: translateY(-50%); + width: 12px; + height: 12px; + border: 2px solid rgba(201, 170, 113, 0.3); + border-top: 2px solid rgba(201, 170, 113, 0.8); + border-radius: 50%; + animation: spin 1s linear infinite; + } + } + + &:disabled { + cursor: not-allowed; + opacity: 0.6; + } + + span { + display: inline-block; + } + } + + @keyframes spin { + 0% { + transform: translateY(-50%) rotate(0deg); + } + 100% { + transform: translateY(-50%) rotate(360deg); + } + } + + &__sort-separator { + color: rgba(255, 255, 255, 0.3); + font-size: 14px; } &__collapse-button { diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.tsx b/src/renderer/src/pages/profile/profile-content/profile-content.tsx index f1bb2ab8..f330bf97 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -3,8 +3,15 @@ import { useContext, useEffect, useMemo, useRef, useState } from "react"; import { ProfileHero } from "../profile-hero/profile-hero"; import { useAppDispatch, useFormat } from "@renderer/hooks"; import { setHeaderTitle } from "@renderer/features"; -import { TelescopeIcon, ChevronRightIcon } from "@primer/octicons-react"; +import { + TelescopeIcon, + ChevronRightIcon, + TrophyIcon, + ClockIcon, + HistoryIcon, +} from "@primer/octicons-react"; import { useTranslation } from "react-i18next"; +import { UserGame } from "@types"; import { LockedProfile } from "./locked-profile"; import { ReportProfile } from "../report-profile/report-profile"; import { FriendsBox } from "./friends-box"; @@ -59,6 +66,35 @@ const gameCardVariants = { ease: [0.25, 0.1, 0.25, 1], }, }, + exit: { + opacity: 0, + y: -20, + scale: 0.95, + transition: { + duration: 0.3, + ease: [0.25, 0.1, 0.25, 1], + }, + }, +}; + +const gameGridVariants = { + hidden: { + opacity: 0, + }, + visible: { + opacity: 1, + transition: { + duration: 0.3, + staggerChildren: 0.1, + delayChildren: 0.1, + }, + }, + exit: { + opacity: 0, + transition: { + duration: 0.2, + }, + }, }; const chevronVariants = { @@ -78,11 +114,23 @@ const chevronVariants = { }, }; +type SortOption = "playtime" | "achievementCount" | "playedRecently"; + export function ProfileContent() { - const { userProfile, isMe, userStats, libraryGames, pinnedGames } = - useContext(userProfileContext); + const { + userProfile, + isMe, + userStats, + libraryGames, + pinnedGames, + getUserLibraryGames, + } = useContext(userProfileContext); const [statsIndex, setStatsIndex] = useState(0); const [isAnimationRunning, setIsAnimationRunning] = useState(true); + const [sortBy, setSortBy] = useState("playedRecently"); + const [isLoadingSort, setIsLoadingSort] = useState(false); + const [prevLibraryGames, setPrevLibraryGames] = useState([]); + const [prevPinnedGames, setPrevPinnedGames] = useState([]); const statsAnimation = useRef(-1); const { toggleSection, isPinnedCollapsed } = useSectionCollapse(); @@ -98,6 +146,15 @@ export function ProfileContent() { } }, [userProfile, dispatch]); + useEffect(() => { + if (userProfile) { + setIsLoadingSort(true); + getUserLibraryGames(sortBy).finally(() => { + setIsLoadingSort(false); + }); + } + }, [sortBy, getUserLibraryGames, userProfile]); + const handleOnMouseEnterGameCard = () => { setIsAnimationRunning(false); }; @@ -129,10 +186,68 @@ export function ProfileContent() { const { numberFormatter } = useFormat(); + // Function to check if game lists have changed + const gamesHaveChanged = ( + current: UserGame[], + previous: UserGame[] + ): boolean => { + if (current.length !== previous.length) return true; + return current.some( + (game, index) => game.objectId !== previous[index]?.objectId + ); + }; + + // Check if animations should run + const shouldAnimateLibrary = gamesHaveChanged(libraryGames, prevLibraryGames); + const shouldAnimatePinned = gamesHaveChanged(pinnedGames, prevPinnedGames); + + // Update previous games when lists change + useEffect(() => { + setPrevLibraryGames(libraryGames); + }, [libraryGames]); + + useEffect(() => { + setPrevPinnedGames(pinnedGames); + }, [pinnedGames]); + const usersAreFriends = useMemo(() => { return userProfile?.relation?.status === "ACCEPTED"; }, [userProfile]); + const SortOptions = () => ( +
+ Sort by: +
+ + | + + | + +
+
+ ); + const content = useMemo(() => { if (!userProfile) return null; @@ -154,6 +269,8 @@ export function ProfileContent() { return (
+ {hasAnyGames && } + {!hasAnyGames && (
@@ -188,10 +305,10 @@ export function ProfileContent() {

{t("pinned")}

+ + {pinnedGames.length} +
- - {pinnedGames.length} -
@@ -204,25 +321,57 @@ export function ProfileContent() { exit="collapsed" layout > -
    - {pinnedGames?.map((game, index) => ( - - - - ))} -
+ + {shouldAnimatePinned ? ( + + {pinnedGames?.map((game, index) => ( + + + + ))} + + ) : ( + pinnedGames?.map((game) => ( +
  • + +
  • + )) + )} +
    )}
    @@ -234,33 +383,62 @@ export function ProfileContent() {

    {t("library")}

    + {userStats && ( + + {numberFormatter.format(userStats.libraryCount)} + + )}
    - {userStats && ( - - {numberFormatter.format(userStats.libraryCount)} - - )}
    -
      - {libraryGames?.map((game, index) => ( - - - - ))} -
    + + {shouldAnimateLibrary ? ( + + {libraryGames?.map((game, index) => ( + + + + ))} + + ) : ( + libraryGames?.map((game) => ( +
  • + +
  • + )) + )} +
    )}
    diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index e00b5863..860c6758 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -38,7 +38,6 @@ export function UserLibraryGameCard({ const { userProfile, isMe, getUserLibraryGames } = useContext(userProfileContext); const { t } = useTranslation("user_profile"); - const { t: tGame } = useTranslation("game_details"); const { numberFormatter } = useFormat(); const { showSuccessToast } = useToast(); const navigate = useNavigate(); @@ -99,23 +98,19 @@ export function UserLibraryGameCard({ setIsPinning(true); try { - if (game.isPinned) { - await window.electron - .removeGameFromPinned(game.shop, game.objectId) - .then(() => { - showSuccessToast(tGame("game_removed_from_pinned")); - }); - } else { - await window.electron - .addGameToPinned(game.shop, game.objectId) - .then(() => { - showSuccessToast(tGame("game_added_to_pinned")); - }); - } - - await new Promise((resolve) => setTimeout(resolve, 1000)); + await window.electron.toggleGamePin( + game.shop, + game.objectId, + !game.isPinned + ); await getUserLibraryGames(); + + if (game.isPinned) { + showSuccessToast(t("game_removed_from_pinned")); + } else { + showSuccessToast(t("game_added_to_pinned")); + } } finally { setIsPinning(false); } diff --git a/src/types/level.types.ts b/src/types/level.types.ts index da702b70..c5bd3454 100644 --- a/src/types/level.types.ts +++ b/src/types/level.types.ts @@ -44,7 +44,7 @@ export interface Game { executablePath?: string | null; launchOptions?: string | null; favorite?: boolean; - pinned?: boolean; + isPinned?: boolean; pinnedDate?: Date | null; automaticCloudSync?: boolean; hasManuallyUpdatedPlaytime?: boolean; From 2cebc73789bcc656f2d3e429d010d40afc992b9e Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Sun, 28 Sep 2025 00:50:51 +0100 Subject: [PATCH 24/27] feat: adding profile sorting --- .../profile-content/profile-content.scss | 86 ---------- .../profile-content/profile-content.tsx | 158 ++---------------- 2 files changed, 16 insertions(+), 228 deletions(-) diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.scss b/src/renderer/src/pages/profile/profile-content/profile-content.scss index 7faae2db..2f274e11 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.scss +++ b/src/renderer/src/pages/profile/profile-content/profile-content.scss @@ -77,92 +77,6 @@ flex-shrink: 0; } - &__sort-container { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: calc(globals.$spacing-unit); - margin-bottom: calc(globals.$spacing-unit * 2); - } - - &__sort-label { - color: rgba(255, 255, 255, 0.6); - font-size: 14px; - font-weight: 400; - } - - &__sort-options { - display: flex; - align-items: center; - gap: calc(globals.$spacing-unit); - font-size: 14px; - } - - &__sort-option { - background: none; - border: none; - color: rgba(255, 255, 255, 0.4); - cursor: pointer; - padding: 4px 0; - font-size: 14px; - font-weight: 300; - transition: all ease 0.2s; - display: flex; - align-items: center; - gap: 6px; - - &:hover:not(:disabled) { - color: rgba(255, 255, 255, 0.6); - } - - &.active { - color: rgba(255, 255, 255, 0.9); - font-weight: 500; - } - - &.loading { - color: rgba(201, 170, 113, 0.8); - font-weight: 500; - position: relative; - - &::after { - content: ""; - position: absolute; - right: -20px; - top: 50%; - transform: translateY(-50%); - width: 12px; - height: 12px; - border: 2px solid rgba(201, 170, 113, 0.3); - border-top: 2px solid rgba(201, 170, 113, 0.8); - border-radius: 50%; - animation: spin 1s linear infinite; - } - } - - &:disabled { - cursor: not-allowed; - opacity: 0.6; - } - - span { - display: inline-block; - } - } - - @keyframes spin { - 0% { - transform: translateY(-50%) rotate(0deg); - } - 100% { - transform: translateY(-50%) rotate(360deg); - } - } - - &__sort-separator { - color: rgba(255, 255, 255, 0.3); - font-size: 14px; - } &__collapse-button { background: none; diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.tsx b/src/renderer/src/pages/profile/profile-content/profile-content.tsx index f330bf97..8de16d3d 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.tsx +++ b/src/renderer/src/pages/profile/profile-content/profile-content.tsx @@ -3,13 +3,7 @@ import { useContext, useEffect, useMemo, useRef, useState } from "react"; import { ProfileHero } from "../profile-hero/profile-hero"; import { useAppDispatch, useFormat } from "@renderer/hooks"; import { setHeaderTitle } from "@renderer/features"; -import { - TelescopeIcon, - ChevronRightIcon, - TrophyIcon, - ClockIcon, - HistoryIcon, -} from "@primer/octicons-react"; +import { TelescopeIcon, ChevronRightIcon } from "@primer/octicons-react"; import { useTranslation } from "react-i18next"; import { UserGame } from "@types"; import { LockedProfile } from "./locked-profile"; @@ -18,102 +12,18 @@ import { FriendsBox } from "./friends-box"; import { RecentGamesBox } from "./recent-games-box"; import { UserStatsBox } from "./user-stats-box"; import { UserLibraryGameCard } from "./user-library-game-card"; +import { SortOptions } from "./sort-options"; import { useSectionCollapse } from "@renderer/hooks/use-section-collapse"; import { motion, AnimatePresence } from "framer-motion"; +import { + sectionVariants, + gameCardVariants, + gameGridVariants, + chevronVariants, + GAME_STATS_ANIMATION_DURATION_IN_MS, +} from "./profile-animations"; import "./profile-content.scss"; -const GAME_STATS_ANIMATION_DURATION_IN_MS = 3500; - -const sectionVariants = { - collapsed: { - opacity: 0, - y: -20, - height: 0, - transition: { - duration: 0.3, - ease: [0.25, 0.1, 0.25, 1], - opacity: { duration: 0.1 }, - y: { duration: 0.1 }, - height: { duration: 0.2 }, - }, - }, - expanded: { - opacity: 1, - y: 0, - height: "auto", - transition: { - duration: 0.3, - ease: [0.25, 0.1, 0.25, 1], - opacity: { duration: 0.2, delay: 0.1 }, - y: { duration: 0.3 }, - height: { duration: 0.3 }, - }, - }, -}; - -const gameCardVariants = { - hidden: { - opacity: 0, - y: 20, - scale: 0.95, - }, - visible: { - opacity: 1, - y: 0, - scale: 1, - transition: { - duration: 0.4, - ease: [0.25, 0.1, 0.25, 1], - }, - }, - exit: { - opacity: 0, - y: -20, - scale: 0.95, - transition: { - duration: 0.3, - ease: [0.25, 0.1, 0.25, 1], - }, - }, -}; - -const gameGridVariants = { - hidden: { - opacity: 0, - }, - visible: { - opacity: 1, - transition: { - duration: 0.3, - staggerChildren: 0.1, - delayChildren: 0.1, - }, - }, - exit: { - opacity: 0, - transition: { - duration: 0.2, - }, - }, -}; - -const chevronVariants = { - collapsed: { - rotate: 0, - transition: { - duration: 0.2, - ease: "easeInOut", - }, - }, - expanded: { - rotate: 90, - transition: { - duration: 0.2, - ease: "easeInOut", - }, - }, -}; - type SortOption = "playtime" | "achievementCount" | "playedRecently"; export function ProfileContent() { @@ -128,7 +38,6 @@ export function ProfileContent() { const [statsIndex, setStatsIndex] = useState(0); const [isAnimationRunning, setIsAnimationRunning] = useState(true); const [sortBy, setSortBy] = useState("playedRecently"); - const [isLoadingSort, setIsLoadingSort] = useState(false); const [prevLibraryGames, setPrevLibraryGames] = useState([]); const [prevPinnedGames, setPrevPinnedGames] = useState([]); const statsAnimation = useRef(-1); @@ -148,10 +57,7 @@ export function ProfileContent() { useEffect(() => { if (userProfile) { - setIsLoadingSort(true); - getUserLibraryGames(sortBy).finally(() => { - setIsLoadingSort(false); - }); + getUserLibraryGames(sortBy); } }, [sortBy, getUserLibraryGames, userProfile]); @@ -186,7 +92,6 @@ export function ProfileContent() { const { numberFormatter } = useFormat(); - // Function to check if game lists have changed const gamesHaveChanged = ( current: UserGame[], previous: UserGame[] @@ -197,11 +102,9 @@ export function ProfileContent() { ); }; - // Check if animations should run const shouldAnimateLibrary = gamesHaveChanged(libraryGames, prevLibraryGames); const shouldAnimatePinned = gamesHaveChanged(pinnedGames, prevPinnedGames); - // Update previous games when lists change useEffect(() => { setPrevLibraryGames(libraryGames); }, [libraryGames]); @@ -214,40 +117,6 @@ export function ProfileContent() { return userProfile?.relation?.status === "ACCEPTED"; }, [userProfile]); - const SortOptions = () => ( -
    - Sort by: -
    - - | - - | - -
    -
    - ); - const content = useMemo(() => { if (!userProfile) return null; @@ -269,7 +138,9 @@ export function ProfileContent() { return (
    - {hasAnyGames && } + {hasAnyGames && ( + + )} {!hasAnyGames && (
    @@ -467,6 +338,9 @@ export function ProfileContent() { pinnedGames, isPinnedCollapsed, toggleSection, + shouldAnimateLibrary, + shouldAnimatePinned, + sortBy, ]); return ( From 92af5c2b23f52b63cd88b2b663d65e774cf17a68 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Sun, 28 Sep 2025 01:01:06 +0100 Subject: [PATCH 25/27] feat: adding profile sorting --- .../profile-content/profile-animations.ts | 91 +++++++++++++++++++ .../profile-content/profile-content.scss | 1 - .../profile/profile-content/sort-options.scss | 56 ++++++++++++ .../profile/profile-content/sort-options.tsx | 45 +++++++++ 4 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 src/renderer/src/pages/profile/profile-content/profile-animations.ts create mode 100644 src/renderer/src/pages/profile/profile-content/sort-options.scss create mode 100644 src/renderer/src/pages/profile/profile-content/sort-options.tsx diff --git a/src/renderer/src/pages/profile/profile-content/profile-animations.ts b/src/renderer/src/pages/profile/profile-content/profile-animations.ts new file mode 100644 index 00000000..f9ac0593 --- /dev/null +++ b/src/renderer/src/pages/profile/profile-content/profile-animations.ts @@ -0,0 +1,91 @@ +export const sectionVariants = { + collapsed: { + opacity: 0, + y: -20, + height: 0, + transition: { + duration: 0.3, + ease: [0.25, 0.1, 0.25, 1], + opacity: { duration: 0.1 }, + y: { duration: 0.1 }, + height: { duration: 0.2 }, + }, + }, + expanded: { + opacity: 1, + y: 0, + height: "auto", + transition: { + duration: 0.3, + ease: [0.25, 0.1, 0.25, 1], + opacity: { duration: 0.2, delay: 0.1 }, + y: { duration: 0.3 }, + height: { duration: 0.3 }, + }, + }, +}; + +export const gameCardVariants = { + hidden: { + opacity: 0, + y: 20, + scale: 0.95, + }, + visible: { + opacity: 1, + y: 0, + scale: 1, + transition: { + duration: 0.4, + ease: [0.25, 0.1, 0.25, 1], + }, + }, + exit: { + opacity: 0, + y: -20, + scale: 0.95, + transition: { + duration: 0.3, + ease: [0.25, 0.1, 0.25, 1], + }, + }, +}; + +export const gameGridVariants = { + hidden: { + opacity: 0, + }, + visible: { + opacity: 1, + transition: { + duration: 0.3, + staggerChildren: 0.1, + delayChildren: 0.1, + }, + }, + exit: { + opacity: 0, + transition: { + duration: 0.2, + }, + }, +}; + +export const chevronVariants = { + collapsed: { + rotate: 0, + transition: { + duration: 0.2, + ease: "easeInOut", + }, + }, + expanded: { + rotate: 90, + transition: { + duration: 0.2, + ease: "easeInOut", + }, + }, +}; + +export const GAME_STATS_ANIMATION_DURATION_IN_MS = 3500; diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.scss b/src/renderer/src/pages/profile/profile-content/profile-content.scss index 2f274e11..bd580b74 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.scss +++ b/src/renderer/src/pages/profile/profile-content/profile-content.scss @@ -77,7 +77,6 @@ flex-shrink: 0; } - &__collapse-button { background: none; border: none; diff --git a/src/renderer/src/pages/profile/profile-content/sort-options.scss b/src/renderer/src/pages/profile/profile-content/sort-options.scss new file mode 100644 index 00000000..e36f3727 --- /dev/null +++ b/src/renderer/src/pages/profile/profile-content/sort-options.scss @@ -0,0 +1,56 @@ +@use "../../../scss/globals.scss"; + +.sort-options { + &__container { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: calc(globals.$spacing-unit); + margin-bottom: calc(globals.$spacing-unit * 2); + } + + &__label { + color: rgba(255, 255, 255, 0.6); + font-size: 14px; + font-weight: 400; + } + + &__options { + display: flex; + align-items: center; + gap: calc(globals.$spacing-unit); + font-size: 14px; + } + + &__option { + background: none; + border: none; + color: rgba(255, 255, 255, 0.4); + cursor: pointer; + padding: 4px 0; + font-size: 14px; + font-weight: 300; + transition: all ease 0.2s; + display: flex; + align-items: center; + gap: 6px; + + &:hover:not(:disabled) { + color: rgba(255, 255, 255, 0.6); + } + + &.active { + color: rgba(255, 255, 255, 0.9); + font-weight: 500; + } + + span { + display: inline-block; + } + } + + &__separator { + color: rgba(255, 255, 255, 0.3); + font-size: 14px; + } +} diff --git a/src/renderer/src/pages/profile/profile-content/sort-options.tsx b/src/renderer/src/pages/profile/profile-content/sort-options.tsx new file mode 100644 index 00000000..53da8e40 --- /dev/null +++ b/src/renderer/src/pages/profile/profile-content/sort-options.tsx @@ -0,0 +1,45 @@ +import { TrophyIcon, ClockIcon, HistoryIcon } from "@primer/octicons-react"; +import { useTranslation } from "react-i18next"; +import "./sort-options.scss"; + +type SortOption = "playtime" | "achievementCount" | "playedRecently"; + +interface SortOptionsProps { + sortBy: SortOption; + onSortChange: (sortBy: SortOption) => void; +} + +export function SortOptions({ sortBy, onSortChange }: SortOptionsProps) { + const { t } = useTranslation("user_profile"); + + return ( +
    + Sort by: +
    + + | + + | + +
    +
    + ); +} From 83e7f676bf59e3a5b37f5463d81f4504a41f5b9b Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Sun, 28 Sep 2025 15:14:25 +0100 Subject: [PATCH 26/27] fix: fixing carousel --- .env.example | 2 - package.json | 4 + src/locales/en/translation.json | 3 - src/locales/pt-BR/translation.json | 3 - src/renderer/src/app.scss | 6 +- src/renderer/src/components/badge/badge.scss | 9 +- src/renderer/src/components/hero/hero.scss | 64 +++- src/renderer/src/components/hero/hero.tsx | 1 + .../game-details/game-details.context.tsx | 5 - .../game-details.context.types.ts | 2 - .../achievements/achievements-content.tsx | 35 +- .../gallery-slider/gallery-slider.scss | 99 ++++-- .../gallery-slider/gallery-slider.tsx | 303 ++++++++++-------- .../game-details/game-details-content.tsx | 30 +- .../src/pages/game-details/game-details.scss | 13 +- .../pages/game-details/hero/hero-panel.scss | 10 +- .../pages/game-details/hero/hero-panel.tsx | 4 +- .../sidebar-section/sidebar-section.scss | 3 +- .../sidebar/game-language-section.scss | 85 +++++ .../sidebar/game-language-section.tsx | 110 ++++--- .../sidebar/game-prices-section.tsx | 86 ----- .../pages/game-details/sidebar/sidebar.scss | 45 ++- .../pages/game-details/sidebar/sidebar.tsx | 6 +- src/renderer/src/pages/home/home.scss | 45 +-- src/renderer/src/pages/home/home.tsx | 2 - .../profile/profile-content/friends-box.tsx | 12 +- .../profile-content/profile-content.scss | 24 ++ .../user-library-game-card.scss | 65 +++- .../src/pages/settings/settings-general.tsx | 8 - src/renderer/src/scss/globals.scss | 10 +- src/types/level.types.ts | 1 - yarn.lock | 86 ++++- 32 files changed, 701 insertions(+), 480 deletions(-) create mode 100644 src/renderer/src/pages/game-details/sidebar/game-language-section.scss delete mode 100755 src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx diff --git a/.env.example b/.env.example index 8ea7af55..3f914eb3 100644 --- a/.env.example +++ b/.env.example @@ -3,5 +3,3 @@ MAIN_VITE_AUTH_URL= MAIN_VITE_WS_URL= RENDERER_VITE_REAL_DEBRID_REFERRAL_ID= RENDERER_VITE_TORBOX_REFERRAL_CODE= -VITE_GG_DEALS_API_URL=https://api.gg.deals/v1/prices/by-steam-app-id -VITE_GG_DEALS_API_KEY= \ No newline at end of file diff --git a/package.json b/package.json index 3186535f..e21c962a 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,8 @@ "diskusage": "^1.2.0", "electron-log": "^5.2.4", "electron-updater": "^6.6.2", + "embla-carousel-autoplay": "^8.6.0", + "embla-carousel-react": "^8.6.0", "file-type": "^20.5.0", "framer-motion": "^12.15.0", "i18next": "^23.11.2", @@ -63,6 +65,8 @@ "lodash-es": "^4.17.21", "parse-torrent": "^11.0.18", "rc-virtual-list": "^3.18.3", + "react-dnd": "^16.0.1", + "react-dnd-html5-backend": "^16.0.1", "react-hook-form": "^7.53.0", "react-i18next": "^14.1.0", "react-loading-skeleton": "^3.4.0", diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 781cd946..ea7fff89 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Successfully signed in" }, "home": { - "featured": "Featured", "surprise_me": "Surprise me", "no_results": "No results found", "start_typing": "Starting typing to search...", @@ -241,7 +240,6 @@ "keyshop_price": "Keyshop price", "historical_retail": "Historical retail", "historical_keyshop": "Historical keyshop", - "supported_languages": "Supported languages", "language": "Language", "caption": "Caption", "audio": "Audio" @@ -290,7 +288,6 @@ "change": "Update", "notifications": "Notifications", "enable_download_notifications": "When a download is complete", - "gg_deals_api_key_description": "gg deals api key. Used to show the lowest price. (https://gg.deals/api/)", "enable_repack_list_notifications": "When a new repack is added", "real_debrid_api_token_label": "Real-Debrid API token", "quit_app_instead_hiding": "Don't hide Hydra when closing", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index fd6fbd97..7f7f8cc1 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -4,7 +4,6 @@ "successfully_signed_in": "Autenticado com sucesso" }, "home": { - "featured": "Destaques", "hot": "Populares", "weekly": "📅 Mais baixados da semana", "achievements": "🏆 Pra platinar", @@ -218,7 +217,6 @@ "keyshop_price": "Preço em keyshops", "historical_retail": "Preço histórico de lojas oficiais", "historical_keyshop": "Preço histórico em keyshops", - "supported_languages": "Idiomas suportados", "language": "Idioma", "caption": "Legenda", "audio": "Áudio" @@ -267,7 +265,6 @@ "change": "Explorar...", "notifications": "Notificações", "enable_download_notifications": "Quando um download for concluído", - "gg_deals_api_key_description": "gg deals api key. Usado para mostrar o menor preço. (https://gg.deals/api/)", "enable_repack_list_notifications": "Quando a lista de repacks for atualizada", "real_debrid_api_token_label": "Token de API do Real-Debrid", "quit_app_instead_hiding": "Encerrar o Hydra em vez de apenas minimizá-lo ao fechar", diff --git a/src/renderer/src/app.scss b/src/renderer/src/app.scss index 18d46dd4..4c5374e8 100644 --- a/src/renderer/src/app.scss +++ b/src/renderer/src/app.scss @@ -10,16 +10,16 @@ } ::-webkit-scrollbar-track { - background-color: rgba(255, 255, 255, 0.03); + background-color: rgba(0, 0, 0, 0.2); } ::-webkit-scrollbar-thumb { - background-color: rgba(255, 255, 255, 0.08); + background-color: rgba(255, 255, 255, 0.15); border-radius: 24px; } ::-webkit-scrollbar-thumb:hover { - background-color: rgba(255, 255, 255, 0.16); + background-color: rgba(255, 255, 255, 0.25); } html, diff --git a/src/renderer/src/components/badge/badge.scss b/src/renderer/src/components/badge/badge.scss index 69c43b3e..f90f8749 100644 --- a/src/renderer/src/components/badge/badge.scss +++ b/src/renderer/src/components/badge/badge.scss @@ -4,9 +4,14 @@ color: globals.$muted-color; font-size: 10px; padding: calc(globals.$spacing-unit / 2) globals.$spacing-unit; - border: solid 1px globals.$muted-color; - border-radius: 4px; + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 6px; display: flex; gap: 4px; align-items: center; + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + transition: all ease 0.2s; } diff --git a/src/renderer/src/components/hero/hero.scss b/src/renderer/src/components/hero/hero.scss index ea14c059..f9ec4d36 100644 --- a/src/renderer/src/components/hero/hero.scss +++ b/src/renderer/src/components/hero/hero.scss @@ -2,16 +2,36 @@ .hero { width: 100%; - height: 280px; - min-height: 280px; - max-height: 280px; - border-radius: 4px; + height: 180px; + min-height: 150px; + border-radius: 0; color: #dadbe1; overflow: hidden; box-shadow: 0px 0px 15px 0px #000000; cursor: pointer; border: solid 1px globals.$border-color; z-index: 1; + flex-shrink: 0; + + @media (min-width: 480px) { + height: 220px; + min-height: 200px; + } + + @media (min-width: 768px) { + height: 300px; + min-height: 300px; + } + + @media (min-width: 1024px) and (min-height: 800px) { + height: 400px; + min-height: 400px; + } + + @media (min-width: 1024px) and (max-height: 799px) { + height: 300px; + min-height: 250px; + } &__media { object-fit: cover; @@ -47,10 +67,42 @@ &__content { width: 100%; height: 100%; - padding: calc(globals.$spacing-unit * 4) calc(globals.$spacing-unit * 3); - gap: calc(globals.$spacing-unit * 2); + padding: calc(globals.$spacing-unit * 2) calc(globals.$spacing-unit * 2); + gap: calc(globals.$spacing-unit); display: flex; flex-direction: column; justify-content: flex-end; + + @media (min-width: 768px) { + padding: calc(globals.$spacing-unit * 3) calc(globals.$spacing-unit * 3); + gap: calc(globals.$spacing-unit * 1.5); + } + + @media (min-width: 1024px) { + padding: calc(globals.$spacing-unit * 4) calc(globals.$spacing-unit * 3); + gap: calc(globals.$spacing-unit * 2); + } + } + + &__logo { + max-width: 100%; + height: auto; + width: 120px; + + @media (min-width: 480px) { + width: 150px; + } + + @media (min-width: 768px) { + width: 200px; + } + + @media (min-width: 1024px) and (min-height: 800px) { + width: 250px; + } + + @media (min-width: 1024px) and (max-height: 799px) { + width: 200px; + } } } diff --git a/src/renderer/src/components/hero/hero.tsx b/src/renderer/src/components/hero/hero.tsx index f177c598..ce73d144 100644 --- a/src/renderer/src/components/hero/hero.tsx +++ b/src/renderer/src/components/hero/hero.tsx @@ -53,6 +53,7 @@ export function Hero() { width="250px" alt={game.description ?? ""} loading="eager" + className="hero__logo" />

    {game.description}

    diff --git a/src/renderer/src/context/game-details/game-details.context.tsx b/src/renderer/src/context/game-details/game-details.context.tsx index ce2923b2..864fd482 100644 --- a/src/renderer/src/context/game-details/game-details.context.tsx +++ b/src/renderer/src/context/game-details/game-details.context.tsx @@ -38,14 +38,12 @@ export const gameDetailsContext = createContext({ isGameRunning: false, isLoading: false, objectId: undefined, - gameColor: "", showRepacksModal: false, showGameOptionsModal: false, stats: null, achievements: null, hasNSFWContentBlocked: false, lastDownloadedOption: null, - setGameColor: () => {}, selectGameExecutable: async () => null, updateGame: async () => {}, setShowGameOptionsModal: () => {}, @@ -82,7 +80,6 @@ export function GameDetailsContextProvider({ const [stats, setStats] = useState(null); const [isLoading, setIsLoading] = useState(true); - const [gameColor, setGameColor] = useState(""); const [isGameRunning, setIsGameRunning] = useState(false); const [showRepacksModal, setShowRepacksModal] = useState(false); const [showGameOptionsModal, setShowGameOptionsModal] = useState(false); @@ -286,7 +283,6 @@ export function GameDetailsContextProvider({ isGameRunning, isLoading, objectId, - gameColor, showGameOptionsModal, showRepacksModal, stats, @@ -294,7 +290,6 @@ export function GameDetailsContextProvider({ hasNSFWContentBlocked, lastDownloadedOption, setHasNSFWContentBlocked, - setGameColor, selectGameExecutable, updateGame, setShowRepacksModal, diff --git a/src/renderer/src/context/game-details/game-details.context.types.ts b/src/renderer/src/context/game-details/game-details.context.types.ts index 99c7b293..302460b7 100644 --- a/src/renderer/src/context/game-details/game-details.context.types.ts +++ b/src/renderer/src/context/game-details/game-details.context.types.ts @@ -16,14 +16,12 @@ export interface GameDetailsContext { isGameRunning: boolean; isLoading: boolean; objectId: string | undefined; - gameColor: string; showRepacksModal: boolean; showGameOptionsModal: boolean; stats: GameStats | null; achievements: UserAchievement[] | null; hasNSFWContentBlocked: boolean; lastDownloadedOption: GameRepack | null; - setGameColor: React.Dispatch>; selectGameExecutable: () => Promise; updateGame: () => Promise; setShowRepacksModal: React.Dispatch>; diff --git a/src/renderer/src/pages/achievements/achievements-content.tsx b/src/renderer/src/pages/achievements/achievements-content.tsx index 477925c7..e555212c 100644 --- a/src/renderer/src/pages/achievements/achievements-content.tsx +++ b/src/renderer/src/pages/achievements/achievements-content.tsx @@ -119,15 +119,8 @@ export function AchievementsContent({ const containerRef = useRef(null); const [isHeaderStuck, setIsHeaderStuck] = useState(false); - const { - gameTitle, - objectId, - shop, - shopDetails, - achievements, - gameColor, - setGameColor, - } = useContext(gameDetailsContext); + const { gameTitle, objectId, shop, shopDetails, achievements } = + useContext(gameDetailsContext); const dispatch = useAppDispatch(); @@ -136,22 +129,6 @@ export function AchievementsContent({ dispatch(setHeaderTitle(gameTitle)); }, [dispatch, gameTitle]); - const handleHeroLoad = async () => { - const output = await average( - shopDetails?.assets?.libraryHeroImageUrl ?? "", - { - amount: 1, - format: "hex", - } - ); - - const backgroundColor = output - ? (new Color(output).darken(0.7).toString() as string) - : ""; - - setGameColor(backgroundColor); - }; - const onScroll: React.UIEventHandler = (event) => { const heroHeight = heroRef.current?.clientHeight ?? 150; @@ -191,7 +168,6 @@ export function AchievementsContent({ src={shopDetails?.assets?.libraryHeroImageUrl ?? ""} className="achievements-content__achievements-list__image" alt={gameTitle} - onLoad={handleHeroLoad} />
    -
    +
    (null); - const mediaContainerRef = useRef(null); - const { t } = useTranslation("game_details"); const hasScreenshots = shopDetails && shopDetails.screenshots?.length; - const hasMovies = shopDetails && shopDetails.movies?.length; - const mediaCount = useMemo(() => { - if (!shopDetails) return 0; + const [emblaRef, emblaApi] = useEmblaCarousel({ loop: false }); + const [selectedIndex, setSelectedIndex] = useState(0); - if (shopDetails.screenshots && shopDetails.movies) { - return shopDetails.screenshots.length + shopDetails.movies.length; - } else if (shopDetails.movies) { - return shopDetails.movies.length; - } else if (shopDetails.screenshots) { - return shopDetails.screenshots.length; - } + const scrollPrev = useCallback(() => { + if (emblaApi) emblaApi.scrollPrev(); + }, [emblaApi]); - return 0; - }, [shopDetails]); + const scrollNext = useCallback(() => { + if (emblaApi) emblaApi.scrollNext(); + }, [emblaApi]); - const [mediaIndex, setMediaIndex] = useState(0); - const [showArrows, setShowArrows] = useState(false); + const scrollTo = useCallback( + (index: number) => { + if (emblaApi) emblaApi.scrollTo(index); + }, + [emblaApi] + ); - const showNextImage = () => { - setMediaIndex((index: number) => { - if (index === mediaCount - 1) return 0; + const scrollToPreview = useCallback( + (index: number, event: React.MouseEvent) => { + scrollTo(index); - return index + 1; - }); - }; + const button = event.currentTarget; + const previewContainer = button.parentElement; - const showPrevImage = () => { - setMediaIndex((index: number) => { - if (index === 0) return mediaCount - 1; + if (previewContainer) { + const containerRect = previewContainer.getBoundingClientRect(); + const buttonRect = button.getBoundingClientRect(); - return index - 1; - }); - }; + const isOffScreenLeft = buttonRect.left < containerRect.left; + const isOffScreenRight = buttonRect.right > containerRect.right; - useEffect(() => { - setMediaIndex(0); - }, [shopDetails]); - - useEffect(() => { - if (hasMovies && mediaContainerRef.current) { - mediaContainerRef.current.childNodes.forEach((node, index) => { - if (node instanceof HTMLVideoElement) { - if (index !== mediaIndex) { - node.pause(); - } + if (isOffScreenLeft || isOffScreenRight) { + button.scrollIntoView({ + behavior: "smooth", + block: "nearest", + inline: "center", + }); } + } + }, + [scrollTo] + ); + + useEffect(() => { + if (!emblaApi) return; + + let isInitialLoad = true; + + const onSelect = () => { + const newIndex = emblaApi.selectedScrollSnap(); + setSelectedIndex(newIndex); + + if (!isInitialLoad) { + const videos = document.querySelectorAll(".gallery-slider__media"); + videos.forEach((video) => { + if (video instanceof HTMLVideoElement) { + video.pause(); + } + }); + } + + isInitialLoad = false; + }; + + emblaApi.on("select", onSelect); + onSelect(); + + return () => { + emblaApi.off("select", onSelect); + }; + }, [emblaApi]); + + const mediaItems = useMemo(() => { + const items: Array<{ + id: string; + type: "video" | "image"; + src?: string; + poster?: string; + videoSrc?: string; + alt: string; + }> = []; + + if (shopDetails?.movies) { + shopDetails.movies.forEach((video, index) => { + items.push({ + id: String(video.id), + type: "video", + poster: video.thumbnail, + videoSrc: video.mp4.max.startsWith("http://") + ? video.mp4.max.replace("http://", "https://") + : video.mp4.max, + alt: t("video", { number: String(index + 1) }), + }); }); } - }, [hasMovies, mediaContainerRef, mediaIndex]); - useEffect(() => { - if (scrollContainerRef.current) { - const container = scrollContainerRef.current; - const totalWidth = container.scrollWidth - container.clientWidth; - const itemWidth = totalWidth / (mediaCount - 1); - const scrollLeft = mediaIndex * itemWidth; - container.scrollLeft = scrollLeft; + if (shopDetails?.screenshots) { + shopDetails.screenshots.forEach((image, index) => { + items.push({ + id: String(image.id), + type: "image", + src: image.path_full, + alt: t("screenshot", { number: String(index + 1) }), + }); + }); } - }, [shopDetails, mediaIndex, mediaCount]); + + return items; + }, [shopDetails, t]); const previews = useMemo(() => { const screenshotPreviews = shopDetails?.screenshots?.map(({ id, path_thumbnail }) => ({ id, thumbnail: path_thumbnail, + type: "image" as const, })) ?? []; if (shopDetails?.movies) { const moviePreviews = shopDetails.movies.map(({ id, thumbnail }) => ({ id, thumbnail, + type: "video" as const, })); return [...moviePreviews, ...screenshotPreviews]; @@ -93,96 +147,87 @@ export function GallerySlider() { return screenshotPreviews; }, [shopDetails]); + if (!hasScreenshots) { + return null; + } + return ( - <> - {hasScreenshots && ( -
    -
    setShowArrows(true)} - onMouseLeave={() => setShowArrows(false)} - className="gallery-slider__animation-container" - ref={mediaContainerRef} - > - {shopDetails.movies && - shopDetails.movies.map((video) => ( +
    +
    +
    + {mediaItems.map((item) => ( +
    + {item.type === "video" ? ( - ))} - - {hasScreenshots && - shopDetails.screenshots?.map((image, i) => ( + ) : ( {t("screenshot", - ))} - - - - -
    - -
    - {previews.map((media, i) => ( - - ))} -
    + )} +
    + ))}
    - )} - + + + + +
    + +
    + {previews.map((media, i) => ( + + ))} +
    +
    ); } 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 40436614..1ce80da4 100644 --- a/src/renderer/src/pages/game-details/game-details-content.tsx +++ b/src/renderer/src/pages/game-details/game-details-content.tsx @@ -1,6 +1,4 @@ import { useContext, useEffect, useMemo, useRef, useState } from "react"; -import { average } from "color.js"; -import Color from "color"; import { HeroPanel } from "./hero"; import { DescriptionHeader } from "./description-header/description-header"; @@ -21,14 +19,8 @@ export function GameDetailsContent() { const { t } = useTranslation("game_details"); - const { - objectId, - shopDetails, - game, - gameColor, - setGameColor, - hasNSFWContentBlocked, - } = useContext(gameDetailsContext); + const { objectId, shopDetails, game, hasNSFWContentBlocked } = + useContext(gameDetailsContext); const { showHydraCloudModal } = useSubscription(); @@ -58,22 +50,6 @@ export function GameDetailsContent() { const [backdropOpacity, setBackdropOpacity] = useState(1); - const handleHeroLoad = async () => { - const output = await average( - shopDetails?.assets?.libraryHeroImageUrl ?? "", - { - amount: 1, - format: "hex", - } - ); - - const backgroundColor = output - ? new Color(output).darken(0.7).toString() - : ""; - - setGameColor(backgroundColor); - }; - useEffect(() => { setBackdropOpacity(1); }, [objectId]); @@ -106,12 +82,10 @@ export function GameDetailsContent() { src={shopDetails?.assets?.libraryHeroImageUrl ?? ""} className="game-details__hero-image" alt={game?.title} - onLoad={handleHeroLoad} />
    diff --git a/src/renderer/src/pages/game-details/game-details.scss b/src/renderer/src/pages/game-details/game-details.scss index 899d654a..e488db5f 100644 --- a/src/renderer/src/pages/game-details/game-details.scss +++ b/src/renderer/src/pages/game-details/game-details.scss @@ -18,7 +18,6 @@ $hero-height: 300px; &__wrapper { display: flex; flex-direction: column; - overflow: hidden; width: 100%; height: 100%; transition: all ease 0.3s; @@ -64,8 +63,8 @@ $hero-height: 300px; &__hero-image { width: 100%; - height: $hero-height; - min-height: $hero-height; + height: calc($hero-height + 72px); + min-height: calc($hero-height + 72px); object-fit: cover; object-position: top; transition: all ease 0.2s; @@ -74,8 +73,8 @@ $hero-height: 300px; @media (min-width: 1250px) { object-position: center; - height: 350px; - min-height: 350px; + height: calc(350px + 72px); + min-height: calc(350px + 72px); } } @@ -97,7 +96,6 @@ $hero-height: 300px; height: 100%; display: flex; flex-direction: column; - overflow: auto; z-index: 1; } @@ -105,6 +103,7 @@ $hero-height: 300px; display: flex; width: 100%; flex: 1; + min-width: 0; background: linear-gradient( 0deg, globals.$background-color 50%, @@ -115,6 +114,8 @@ $hero-height: 300px; &__description-content { width: 100%; height: 100%; + min-width: 0; + flex: 1; } &__description { diff --git a/src/renderer/src/pages/game-details/hero/hero-panel.scss b/src/renderer/src/pages/game-details/hero/hero-panel.scss index 066ce196..4dd1cc22 100644 --- a/src/renderer/src/pages/game-details/hero/hero-panel.scss +++ b/src/renderer/src/pages/game-details/hero/hero-panel.scss @@ -5,18 +5,24 @@ height: 72px; min-height: 72px; padding: calc(globals.$spacing-unit * 2) calc(globals.$spacing-unit * 3); - background-color: globals.$dark-background-color; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: solid 1px rgba(255, 255, 255, 0.15); display: flex; align-items: center; justify-content: space-between; transition: all ease 0.2s; border-bottom: solid 1px globals.$border-color; - position: sticky; overflow: hidden; top: 0; z-index: 2; + box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); &--stuck { + background: rgba(0, 0, 0, 0.7); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); box-shadow: 0px 0px 15px 0px rgba(0, 0, 0, 0.8); } diff --git a/src/renderer/src/pages/game-details/hero/hero-panel.tsx b/src/renderer/src/pages/game-details/hero/hero-panel.tsx index 3a07daa1..7f8de0b0 100644 --- a/src/renderer/src/pages/game-details/hero/hero-panel.tsx +++ b/src/renderer/src/pages/game-details/hero/hero-panel.tsx @@ -14,7 +14,7 @@ export function HeroPanel() { const { formatDate } = useDate(); - const { game, repacks, gameColor } = useContext(gameDetailsContext); + const { game, repacks } = useContext(gameDetailsContext); const { lastPacket } = useDownload(); @@ -50,7 +50,7 @@ export function HeroPanel() { game?.download?.status === "paused"; return ( -
    +
    {getInfo()}
    diff --git a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss index 5ea421c3..8674b044 100644 --- a/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss +++ b/src/renderer/src/pages/game-details/sidebar-section/sidebar-section.scss @@ -2,8 +2,7 @@ .sidebar-section { &__button { - height: 72px; - padding: calc(globals.$spacing-unit * 2) calc(globals.$spacing-unit * 2); + padding: calc(globals.$spacing-unit * 2.5) calc(globals.$spacing-unit * 2); display: flex; align-items: center; background-color: globals.$background-color; diff --git a/src/renderer/src/pages/game-details/sidebar/game-language-section.scss b/src/renderer/src/pages/game-details/sidebar/game-language-section.scss new file mode 100644 index 00000000..896316ec --- /dev/null +++ b/src/renderer/src/pages/game-details/sidebar/game-language-section.scss @@ -0,0 +1,85 @@ +@use "../../../scss/globals.scss"; + +.game-language-section { + background-color: rgba(255, 255, 255, 0.02); + overflow: hidden; + + &__header { + display: flex; + background-color: rgba(255, 255, 255, 0.05); + border-bottom: 1px solid globals.$border-color; + } + + &__header-item { + display: flex; + align-items: center; + color: globals.$muted-color; + font-size: globals.$small-font-size; + font-weight: 600; + flex: 1; + padding: calc(globals.$spacing-unit * 1.5) calc(globals.$spacing-unit * 2.5); + + &--center { + justify-content: flex-start; + flex: 0 0 60px; + } + } + + &__content { + display: flex; + flex-direction: column; + } + + &__row { + display: flex; + transition: background-color 0.2s ease; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); + + &:hover { + background-color: rgba(255, 255, 255, 0.03); + } + + &:last-child { + border-bottom: none; + } + } + + &__cell { + padding: calc(globals.$spacing-unit * 1.5) calc(globals.$spacing-unit * 2.5); + font-size: globals.$body-font-size; + color: globals.$body-color; + display: flex; + align-items: center; + flex: 1; + + &--language { + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + &--center { + justify-content: flex-start; + flex: 0 0 60px; + } + } + + &__check { + color: globals.$body-color; + opacity: 0.8; + } + + &__cross { + color: globals.$body-color; + opacity: 0.8; + } + + @media (max-width: 320px) { + &__header, + &__cell { + padding: calc(globals.$spacing-unit * 1) calc(globals.$spacing-unit * 0.5); + font-size: calc(globals.$small-font-size * 0.9); + } + } +} diff --git a/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx b/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx index f67e4dfa..874a588e 100755 --- a/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx +++ b/src/renderer/src/pages/game-details/sidebar/game-language-section.tsx @@ -1,65 +1,71 @@ -import { useContext } from "react"; +import { useContext, useMemo } from "react"; import { useTranslation } from "react-i18next"; +import { CheckIcon, XIcon } from "@primer/octicons-react"; import { gameDetailsContext } from "@renderer/context/game-details/game-details.context"; import { SidebarSection } from "../sidebar-section/sidebar-section"; +import "./game-language-section.scss"; export function GameLanguageSection() { const { t } = useTranslation("game_details"); - const { shopDetails, objectId } = useContext(gameDetailsContext); + const { shopDetails } = useContext(gameDetailsContext); - const getLanguages = () => { - let languages = shopDetails?.supported_languages; - if (!languages) return []; - languages = languages?.split("
    ")[0]; - const arrayIdiomas = languages?.split(","); - const listLanguages: { - language: string; - caption: string; - audio: string; - }[] = []; - arrayIdiomas?.forEach((lang) => { - const objectLanguage = { - language: lang.replace("*", ""), - caption: "✔", - audio: lang.includes("*") ? "✔" : "", - }; - listLanguages.push(objectLanguage); - }); - return listLanguages; - }; + const languages = useMemo(() => { + const supportedLanguages = shopDetails?.supported_languages; + if (!supportedLanguages) return []; + + const languagesString = supportedLanguages.split("
    ")[0]; + const languageArray = languagesString?.split(",") || []; + + return languageArray.map((lang) => ({ + language: lang.replace("*", "").trim(), + hasAudio: lang.includes("*"), + })); + }, [shopDetails?.supported_languages]); + + if (languages.length === 0) { + return null; + } return ( -
    -

    {t("supported_languages")}

    - - - - - - - - - - {getLanguages().map((lang) => ( - - - - - - ))} - -
    {t("language")}{t("caption")}{t("audio")}
    {lang.language}{lang.caption}{lang.audio}
    -
    -
    - - Link Steam - +
    +
    +
    + {t("language")} +
    +
    + {t("caption")} +
    +
    + {t("audio")} +
    +
    + +
    + {languages.map((lang) => ( +
    +
    + {lang.language} +
    +
    + +
    +
    + {lang.hasAudio ? ( + + ) : ( + + )} +
    +
    + ))} +
    ); diff --git a/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx b/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx deleted file mode 100755 index 0753cdad..00000000 --- a/src/renderer/src/pages/game-details/sidebar/game-prices-section.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import { useCallback, useContext, useEffect, useState } from "react"; -import { SidebarSection } from "../sidebar-section/sidebar-section"; -import { useTranslation } from "react-i18next"; -import { gameDetailsContext } from "@renderer/context/game-details/game-details.context"; -import { useAppSelector } from "@renderer/hooks"; - -export function GamePricesSection() { - const userPreferences = useAppSelector( - (state) => state.userPreferences.value - ); - const { t } = useTranslation("game_details"); - const [priceData, setPriceData] = useState(null); - const [isLoadingPrices, setIsLoadingPrices] = useState(false); - const { objectId } = useContext(gameDetailsContext); - - const fetchGamePrices = useCallback(async (steamAppId: string) => { - setIsLoadingPrices(true); - try { - const apiKey = - userPreferences?.ggDealsApiKey || import.meta.env.VITE_GG_DEALS_API_KEY; - if (!apiKey) { - setPriceData(null); - setIsLoadingPrices(false); - return; - } - const url = `${import.meta.env.VITE_GG_DEALS_API_URL}/?ids=${steamAppId}&key=${apiKey}®ion=br`; - const response = await fetch(url); - if (!response.ok) { - throw new Error("Network response was not ok"); - } - const data = await response.json(); - setPriceData(data.data?.[steamAppId] ?? null); - } catch (error) { - setPriceData(null); - } finally { - setIsLoadingPrices(false); - } - }, []); - - useEffect(() => { - if (objectId) { - fetchGamePrices(objectId.toString()); - } - }, [objectId, fetchGamePrices]); - - return ( - - {isLoadingPrices ? ( -
    {t("loading")}
    - ) : priceData ? ( -
    -
      -
    • - {t("retail_price")}: {t("currency_symbol")} - {priceData.prices.currentRetail} -
    • -
    • - {t("keyshop_price")}: {t("currency_symbol")} - {priceData.prices.currentKeyshops} -
    • -
    • - {t("historical_retail")}: {t("currency_symbol")} - {priceData.prices.historicalRetail} -
    • -
    • - {t("historical_keyshop")}: {t("currency_symbol")} - {priceData.prices.historicalKeyshops} -
    • -
    • - - {t("view_all_prices")} - -
    • -
    -
    - ) : ( -
    {t("no_prices_found")}
    - )} -
    - ); -} diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.scss b/src/renderer/src/pages/game-details/sidebar/sidebar.scss index 84386f12..d1c54f84 100755 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.scss +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.scss @@ -3,17 +3,30 @@ .content-sidebar { border-left: solid 1px globals.$border-color; background-color: globals.$dark-background-color; - width: 100%; height: 100%; + flex-shrink: 0; + width: 280px; @media (min-width: 1024px) { - max-width: 300px; - width: 100%; + width: 320px; } @media (min-width: 1280px) { - width: 100%; - max-width: 400px; + width: 380px; + } + + @media (min-width: 1440px) { + width: 420px; + } + + @media (max-width: 768px) { + width: 35%; + min-width: 220px; + } + + @media (max-width: 480px) { + width: 40%; + min-width: 200px; } } @@ -194,25 +207,3 @@ .achievements-placeholder__blur { filter: blur(4px); } - -.table-languages { - width: 100%; - border-collapse: collapse; - text-align: left; - - th, - td { - padding: globals.$spacing-unit; - border-bottom: solid 1px globals.$border-color; - } - - th { - font-size: globals.$small-font-size; - color: globals.$muted-color; - font-weight: normal; - } - - td { - font-size: globals.$body-font-size; - } -} diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx index af72dc92..0a24c418 100755 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx @@ -21,7 +21,6 @@ import { SidebarSection } from "../sidebar-section/sidebar-section"; import { buildGameAchievementPath } from "@renderer/helpers"; import { useSubscription } from "@renderer/hooks/use-subscription"; import "./sidebar.scss"; -import { GamePricesSection } from "./game-prices-section"; import { GameLanguageSection } from "./game-language-section"; const achievementsPlaceholder: UserAchievement[] = [ @@ -117,9 +116,6 @@ export function Sidebar() { return ( ); } diff --git a/src/renderer/src/pages/home/home.scss b/src/renderer/src/pages/home/home.scss index 878b84f1..497f074e 100644 --- a/src/renderer/src/pages/home/home.scss +++ b/src/renderer/src/pages/home/home.scss @@ -6,8 +6,8 @@ height: 100%; display: flex; flex-direction: column; - gap: calc(globals.$spacing-unit * 3); - padding: calc(globals.$spacing-unit * 3); + gap: calc(globals.$spacing-unit * 2); + padding: 0; flex: 1; overflow-y: auto; } @@ -17,6 +17,7 @@ gap: globals.$spacing-unit; justify-content: space-between; align-items: center; + padding: calc(globals.$spacing-unit * 3); } &__buttons-list { @@ -27,25 +28,6 @@ gap: globals.$spacing-unit; } - &__cards { - display: grid; - grid-template-columns: repeat(1, 1fr); - gap: calc(globals.$spacing-unit * 2); - transition: all ease 0.2s; - - @media (min-width: 768px) { - grid-template-columns: repeat(2, 1fr); - } - - @media (min-width: 1250px) { - grid-template-columns: repeat(3, 1fr); - } - - @media (min-width: 1600px) { - grid-template-columns: repeat(4, 1fr); - } - } - &__card-skeleton { width: 100%; height: 180px; @@ -99,5 +81,26 @@ &__title { display: flex; gap: globals.$spacing-unit; + padding: 0 calc(globals.$spacing-unit * 3); + } + + &__cards { + display: grid; + grid-template-columns: repeat(1, 1fr); + gap: calc(globals.$spacing-unit * 2); + transition: all ease 0.2s; + padding: 0 calc(globals.$spacing-unit * 3) calc(globals.$spacing-unit * 3); + + @media (min-width: 768px) { + grid-template-columns: repeat(2, 1fr); + } + + @media (min-width: 1250px) { + grid-template-columns: repeat(3, 1fr); + } + + @media (min-width: 1600px) { + grid-template-columns: repeat(4, 1fr); + } } } diff --git a/src/renderer/src/pages/home/home.tsx b/src/renderer/src/pages/home/home.tsx index ccf81566..e2f66283 100644 --- a/src/renderer/src/pages/home/home.tsx +++ b/src/renderer/src/pages/home/home.tsx @@ -97,8 +97,6 @@ export default function Home() { return (
    -

    {t("featured")}

    -
    diff --git a/src/renderer/src/pages/profile/profile-content/friends-box.tsx b/src/renderer/src/pages/profile/profile-content/friends-box.tsx index 8ab3808a..bee4b35c 100644 --- a/src/renderer/src/pages/profile/profile-content/friends-box.tsx +++ b/src/renderer/src/pages/profile/profile-content/friends-box.tsx @@ -31,10 +31,14 @@ export function FriendsBox() { return (
    -

    {t("friends")}

    - {userStats && ( - {numberFormatter.format(userStats.friendsCount)} - )} +
    +

    {t("friends")}

    + {userStats && ( + + {numberFormatter.format(userStats.friendsCount)} + + )} +
    diff --git a/src/renderer/src/pages/profile/profile-content/profile-content.scss b/src/renderer/src/pages/profile/profile-content/profile-content.scss index bd580b74..c3c71d9a 100644 --- a/src/renderer/src/pages/profile/profile-content/profile-content.scss +++ b/src/renderer/src/pages/profile/profile-content/profile-content.scss @@ -151,5 +151,29 @@ @container #{globals.$app-container} (min-width: 3000px) { grid-template-columns: repeat(12, 1fr); } + + &--drag-over { + background: rgba(255, 255, 255, 0.05); + border: 2px dashed rgba(255, 255, 255, 0.3); + position: relative; + transition: all ease 0.2s; + + &::before { + content: "Drop here to " attr(data-action); + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: globals.$muted-color; + font-size: 14px; + font-weight: 500; + z-index: 10; + pointer-events: none; + background: rgba(0, 0, 0, 0.8); + padding: 8px 16px; + border-radius: 4px; + backdrop-filter: blur(10px); + } + } } } diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss index ab1f3456..f072fdd5 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss @@ -7,10 +7,26 @@ position: relative; display: flex; transition: all ease 0.2s; + cursor: grab; &:hover { transform: scale(1.05); } + + &:active { + cursor: grabbing; + transform: scale(1.02); + } + + &[draggable="true"] { + cursor: grab; + + &:active { + cursor: grabbing; + opacity: 0.8; + transform: scale(1.02) rotate(2deg); + } + } } &__cover { @@ -75,29 +91,47 @@ } &__favorite-icon { - color: white; - background-color: rgba(0, 0, 0, 0.7); + color: rgba(255, 255, 255, 0.8); + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + border: solid 1px rgba(255, 255, 255, 0.15); border-radius: 50%; padding: 6px; display: flex; align-items: center; justify-content: center; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transition: all ease 0.2s; + + &:hover { + background: rgba(0, 0, 0, 0.5); + border-color: rgba(255, 255, 255, 0.25); + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + } } &__pin-button { - color: white; - background-color: rgba(0, 0, 0, 0.7); - border: none; + color: rgba(255, 255, 255, 0.8); + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + border: solid 1px rgba(255, 255, 255, 0.15); border-radius: 50%; padding: 6px; display: flex; align-items: center; justify-content: center; cursor: pointer; - transition: background-color 0.2s ease; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transition: all ease 0.2s; &:hover { - background-color: rgba(0, 0, 0, 0.9); + background: rgba(0, 0, 0, 0.5); + border-color: rgba(255, 255, 255, 0.25); + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } &:disabled { @@ -107,14 +141,25 @@ } &__playtime { - background-color: globals.$background-color; - color: globals.$muted-color; - border: solid 1px globals.$border-color; + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + color: rgba(255, 255, 255, 0.8); + border: solid 1px rgba(255, 255, 255, 0.15); border-radius: 4px; display: flex; align-items: center; gap: 4px; padding: 4px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + transition: all ease 0.2s; + + &:hover { + background: rgba(0, 0, 0, 0.5); + border-color: rgba(255, 255, 255, 0.25); + transform: translateY(-1px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + } } &__manual-playtime { color: globals.$warning-color; diff --git a/src/renderer/src/pages/settings/settings-general.tsx b/src/renderer/src/pages/settings/settings-general.tsx index b952dfa0..c698440d 100644 --- a/src/renderer/src/pages/settings/settings-general.tsx +++ b/src/renderer/src/pages/settings/settings-general.tsx @@ -35,7 +35,6 @@ export function SettingsGeneral() { const [form, setForm] = useState({ downloadsPath: "", - ggDealsApiKey: "", downloadNotificationsEnabled: false, repackUpdatesNotificationsEnabled: false, friendRequestNotificationsEnabled: false, @@ -101,7 +100,6 @@ export function SettingsGeneral() { setForm((prev) => ({ ...prev, downloadsPath: userPreferences.downloadsPath ?? defaultDownloadsPath, - ggDealsApiKey: userPreferences.ggDealsApiKey ?? "", downloadNotificationsEnabled: userPreferences.downloadNotificationsEnabled ?? false, repackUpdatesNotificationsEnabled: @@ -208,12 +206,6 @@ export function SettingsGeneral() { } /> - handleChange({ ggDealsApiKey: e.target.value })} - /> - Date: Sun, 28 Sep 2025 15:17:37 +0100 Subject: [PATCH 27/27] fix: fixing carousel --- src/renderer/src/pages/achievements/achievements-content.tsx | 2 -- .../src/pages/game-details/gallery-slider/gallery-slider.scss | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/renderer/src/pages/achievements/achievements-content.tsx b/src/renderer/src/pages/achievements/achievements-content.tsx index e555212c..ab50f2f1 100644 --- a/src/renderer/src/pages/achievements/achievements-content.tsx +++ b/src/renderer/src/pages/achievements/achievements-content.tsx @@ -9,8 +9,6 @@ import { import { LockIcon, PersonIcon, TrophyIcon } from "@primer/octicons-react"; import { gameDetailsContext } from "@renderer/context"; import type { ComparedAchievements } from "@types"; -import { average } from "color.js"; -import Color from "color"; import { Link } from "@renderer/components"; import { ComparedAchievementList } from "./compared-achievement-list"; import { AchievementList } from "./achievement-list"; diff --git a/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss b/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss index a47e460b..c7932a34 100644 --- a/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss +++ b/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.scss @@ -145,7 +145,7 @@ left: globals.$spacing-unit; transform: translateY(-50%) translateX(-100px); opacity: 0; - + .gallery-slider__viewport:hover & { transform: translateY(-50%) translateX(0); opacity: 1; @@ -156,7 +156,7 @@ right: globals.$spacing-unit; transform: translateY(-50%) translateX(100px); opacity: 0; - + .gallery-slider__viewport:hover & { transform: translateY(-50%) translateX(0); opacity: 1;