diff --git a/.github/pull-request-template.md b/.github/pull-request-template.md new file mode 100644 index 00000000..3653dd16 --- /dev/null +++ b/.github/pull-request-template.md @@ -0,0 +1,12 @@ + + +**When submitting this pull request, I confirm the following (please check the boxes):** + +- [ ] I have read and understood the [Contributor Guidelines](https://github.com/hydralauncher/hydra?tab=readme-ov-file#ways-you-can-contribute). +- [ ] I have checked that there are no duplicate pull requests related to this request. +- [ ] I have considered, and confirm that this submission is valuable to others. +- [ ] I accept that this submission may not be used and the pull request may be closed at the discretion of the maintainers. + +**Fill in the PR content:** + +- diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 5d72b35f..3941496a 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -134,7 +134,7 @@ "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 its conclusion. In case Hydra closes before the conclusion, you will lose your progress.", + "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", "achievement": "Achievement", "achievements_count": "Achievements {{unlockedCount}}/{{achievementsCount}}", @@ -220,7 +220,7 @@ "language": "Language", "real_debrid_api_token": "API Token", "enable_real_debrid": "Enable Real-Debrid", - "real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to download files instantly and at the best of your Internet speed.", + "real_debrid_description": "Real-Debrid is an unrestricted downloader that allows you to quickly download files, only limited by your internet speed.", "real_debrid_invalid_token": "Invalid API token", "real_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", @@ -235,7 +235,7 @@ "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 containing the .json file", + "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", @@ -254,11 +254,12 @@ "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 been already added", + "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 in unlocked" + "enable_achievement_notifications": "When an achievement is unlocked", + "launch_minimized": "Launch Hydra minimized" }, "notifications": { "download_complete": "Download complete", @@ -293,7 +294,7 @@ "amount_hours": "{{amount}} hours", "amount_minutes": "{{amount}} minutes", "last_time_played": "Last played {{period}}", - "activity": "Recent activity", + "activity": "Recent Activity", "library": "Library", "total_play_time": "Total playtime: {{amount}}", "no_recent_activity_title": "Hmmm… nothing here", @@ -332,7 +333,7 @@ "user_block_modal_text": "This will block {{displayName}}", "blocked_users": "Blocked users", "unblock": "Unblock", - "no_friends_added": "You still don't have added friends", + "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", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index aefdd5b6..bb0dc0e3 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -254,7 +254,8 @@ "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" + "enable_achievement_notifications": "Quando uma conquista é desbloqueada", + "launch_minimized": "Iniciar o Hydra minimizado" }, "notifications": { "download_complete": "Download concluído", diff --git a/src/locales/zh/translation.json b/src/locales/zh/translation.json index 6184db65..9bab7516 100644 --- a/src/locales/zh/translation.json +++ b/src/locales/zh/translation.json @@ -6,7 +6,11 @@ "home": { "featured": "特色推荐", "surprise_me": "向我推荐", - "no_results": "没有找到结果" + "no_results": "没有找到结果", + "start_typing": "键入以开始搜素...", + "hot": "当下热门", + "weekly": "📅本周热门游戏", + "achievements": "🏆尝试击败" }, "sidebar": { "catalogue": "游戏目录", @@ -20,7 +24,8 @@ "home": "主页", "queued": "{{title}} (已加入下载队列)", "game_has_no_executable": "未选择游戏的可执行文件", - "sign_in": "登入" + "sign_in": "登入", + "friends": "好友" }, "header": { "search": "搜索游戏", @@ -36,17 +41,18 @@ "no_downloads_in_progress": "没有正在进行的下载", "downloading_metadata": "正在下载{{title}}的元数据…", "downloading": "正在下载{{title}}… ({{percentage}}完成) - 剩余时间{{eta}} - 速度{{speed}}", - "calculating_eta": "正在下载 {{title}}… (已完成{{percentage}}.) - 正在计算剩余时间..." + "calculating_eta": "正在下载 {{title}}… (已完成{{percentage}}.) - 正在计算剩余时间...", + "checking_files": "正在校验 {{title}} 的文件... ({{percentage}} 已完成)" }, "catalogue": { "next_page": "下一页", "previous_page": "上一页" }, "game_details": { - "open_download_options": "打开下载选项", - "download_options_zero": "无下载选项", - "download_options_one": "{{count}}个下载选项", - "download_options_other": "{{count}}个下载选项", + "open_download_options": "打开下载菜单", + "download_options_zero": "无可下载项", + "download_options_one": "{{count}}个可下载项", + "download_options_other": "{{count}}个可下载项", "updated_at": "更新于{{updated_at}}", "install": "安装", "resume": "恢复", @@ -55,11 +61,13 @@ "remove": "移除", "space_left_on_disk": "磁盘剩余空间{{space}}", "eta": "预计完成时间{{eta}}", + "calculating_eta": "正在计算剩余时间…", "downloading_metadata": "正在下载元数据…", "filter": "筛选重打包", "requirements": "配置要求", "minimum": "最低要求", "recommended": "推荐要求", + "paused": "已暂停", "release_date": "发布于{{date}}", "publisher": "发行商{{publisher}}", "hours": "小时", @@ -80,15 +88,18 @@ "playing_now": "正在游戏中", "change": "更改", "repacks_modal_description": "选择您想要下载的重打包", - "select_folder_hint": "要更改默认文件夹,请访问", + "select_folder_hint": "要更改默认文件夹,请访问<0>设置", "download_now": "立即下载", + "no_shop_details": "无法检索商店详细信息.", + "download_options": "下载选项", + "download_path": "下载路径", "previous_screenshot": "上一张截图", "next_screenshot": "下一张截图", "screenshot": "截图 {{number}}", "open_screenshot": "打开截图 {{number}}", "download_settings": "下载设置", "downloader": "下载器", - "select_executable": "选择", + "select_executable": "选择可执行文件", "no_executable_selected": "没有可执行文件被指定", "open_folder": "打开目录", "open_download_location": "查看已下载的文件", @@ -107,7 +118,54 @@ "download_paused": "下载暂停", "last_downloaded_option": "上次下载的选项", "create_shortcut_success": "成功创建快捷方式", - "create_shortcut_error": "创建快捷方式出错" + "create_shortcut_error": "创建快捷方式出错", + "nsfw_content_title": "本游戏包含不适合展示的内容", + "nsfw_content_description": "{{title}}包含可能不适合所有年龄段的内容。您确定要继续吗?", + "allow_nsfw_content": "继续", + "refuse_nsfw_content": "返回", + "stats": "统计数据", + "download_count": "下载量", + "player_count": "活跃玩家", + "download_error": "此下载选项不可用", + "download": "下载", + "executable_path_in_use": "可执行文件已经被以下游戏 \"{{game}}\" 使用", + "warning": "警告:", + "hydra_needs_to_remain_open": "对于此次下载,Hydra必须保持开启直至其完成。若海德拉在完成前关闭,您的进度将丢失。", + "achievements": "成就", + "achievements_count": "成就 {{unlockedCount}}/{{achievementsCount}}", + "cloud_save": "云存档", + "cloud_save_description": "将您的进度保存在云端,便可在任何设备上继续游戏。", + "backups": "备份", + "install_backup": "安装", + "delete_backup": "删除", + "create_backup": "新备份", + "last_backup_date": "最后一次备份于{{date}}", + "no_backup_preview": "未找到此游戏标题的存档", + "restoring_backup": "正在恢复备份({{progress}}已完成)…", + "uploading_backup": "上传备份中…", + "no_backups": "您尚未为这款游戏创建任何备份", + "backup_uploaded": "备份已上传", + "backup_deleted": "备份已删除", + "backup_restored": "备份已恢复", + "see_all_achievements": "查看所有成就", + "sign_in_to_see_achievements": "登入以查看所有成就", + "mapping_method_automatic": "自动", + "mapping_method_manual": "常规", + "mapping_method_label": "索引类型", + "files_automatically_mapped": "文件已自动索引", + "no_backups_created": "没有为此游戏创建过备份", + "manage_files": "管理文件", + "loading_save_preview": "正在查找要保存的游戏…", + "wine_prefix": "Wine 前置", + "wine_prefix_description": "运行该游戏所用的 Wine 前置", + "no_download_option_info": "无可用信息", + "backup_deletion_failed": "删除备份失败", + "max_number_of_artifacts_reached": "已达到该游戏备份上限", + "achievements_not_sync": "你的成就未同步", + "manage_files_description": "管理哪些文件要备份和恢复", + "select_folder": "选择文件夹", + "backup_from": "{{date}} 时备份", + "custom_backup_location_set": "自定义备份文件位置" }, "activation": { "title": "激活 Hydra", @@ -124,6 +182,7 @@ "paused": "已暂停", "verifying": "正在验证…", "completed": "已完成", + "removed": "未下载", "cancel": "取消", "filter": "筛选已下载游戏", "remove": "移除", @@ -138,7 +197,8 @@ "downloads_completed": "已完成", "queued": "下载列表", "no_downloads_title": "空空如也", - "no_downloads_description": "你还未使用Hydra下载任何游戏,但什么时候开始,都为时不晚。" + "no_downloads_description": "你还未使用Hydra下载任何游戏,但什么时候开始,都为时不晚。", + "checking_files": "正在校验文件…" }, "settings": { "downloads_path": "下载路径", @@ -181,14 +241,49 @@ "found_download_option_zero": "未找到下载选项", "found_download_option_one": "找到 {{countFormatted}} 个下载选项", "found_download_option_other": "找到 {{countFormatted}} 个下载选项", - "import": "导入" + "import": "导入", + "public": "公开", + "private": "私密", + "friends_only": "仅限朋友", + "privacy": "隐私", + "profile_visibility": "资料可见性", + "profile_visibility_description": "选择谁可以查看您的个人资料和资料库", + "required_field": "该字段为必填字段", + "source_already_exists": "已添加此来源", + "must_be_valid_url": "来源必须是有效的 URL", + "blocked_users": "已屏蔽用户", + "user_unblocked": "用户已经被屏蔽", + "enable_achievement_notifications": "当成就解锁时" }, - "modal": { - "close": "关闭按钮" + "notifications": { + "download_complete": "下载完成", + "game_ready_to_install": "{{title}} 已准备就绪", + "repack_list_updated": "重打包列表已更新", + "repack_count_one": "{{count}} 重打包已添加", + "repack_count_other": "{{count}} 重打包已添加", + "new_update_available": "版本 {{version}} 可用", + "restart_to_install_update": "重启 Hydra 以安装更新", + "notification_achievement_unlocked_title": "{{game}} 的成绩已解锁", + "notification_achievement_unlocked_body": "{{achievement}} 和其他 {{count}} 已解锁" + }, + "system_tray": { + "open": "打开 Hydra", + "quit": "退出" + }, + "game_card": { + "no_downloads": "无可用下载选项" + }, + "binary_not_found_modal": { + "title": "程序未安装", + "description": "您的系统中找不到 Wine 或 Lutris 的可执行文件", + "instructions": "请检查在 Linux 发行版上安装这些软件的正确方法,以便游戏能够正常运行" }, "forms": { "toggle_password_visibility": "切换密码可见性" }, + "modal": { + "close": "关闭按钮" + }, "user_profile": { "amount_hours": "{{amount}} 小时", "amount_minutes": "{{amount}} 分钟", @@ -208,7 +303,74 @@ "cancel": "取消", "successfully_signed_out": "登出成功", "sign_out": "登出", - "playing_for": "Playing for {{amount}}", - "sign_out_modal_text": "您的资料库与您当前的账户相关联。注销后,您的资料库将不再可见,任何进度也不会保存。继续退出吗?" + "playing_for": "已经玩了{{amount}}", + "sign_out_modal_text": "您的资料库与您当前的账户相关联。注销后,您的资料库将不再可见,任何进度也不会保存。继续退出吗?", + "add_friends": "添加好友", + "add": "添加", + "friend_code": "好友代码", + "see_profile": "查看资料", + "sending": "发送中", + "friend_request_sent": "好友请求已发送", + "friends": "好友", + "friends_list": "好友列表", + "user_not_found": "未找到此用户", + "block_user": "屏蔽此用户", + "add_friend": "添加好友", + "request_sent": "请求已发送", + "request_received": "已收到请求", + "accept_request": "同意申请", + "ignore_request": "忽略申请", + "cancel_request": "取消申请", + "undo_friendship": "解除好友关系", + "request_accepted": "请求已通过", + "user_blocked_successfully": "成功屏蔽此用户", + "user_block_modal_text": "这将会屏蔽 {{displayName}}", + "blocked_users": "黑名单用户", + "unblock": "解除屏蔽", + "no_friends_added": "你还没有添加过好友", + "pending": "待处理", + "no_pending_invites": "您没有待处理的邀请", + "no_blocked_users": "你没有已经拉人黑名单的用户", + "friend_code_copied": "好友代码已复制", + "undo_friendship_modal_text": "这将使你与 {{displayName}} 解除好友关系", + "privacy_hint": "要调整谁可以看到你的个人资料,可以去<0>设置中修改", + "locked_profile": "此个人资料是私密的", + "image_process_failure": "处理图片时发生错误", + "required_field": "此字段为必填项", + "displayname_min_length": "显示名称最少必须为3个字符。", + "displayname_max_length": "显示名称最多必须为50个字符", + "report_profile": "举报此资料", + "report_reason": "为什么你要举报此资料?", + "report_description": "额外信息", + "report_description_placeholder": "额外信息", + "report": "举报", + "report_reason_hate": "Hate speech", + "report_reason_sexual_content": "色情内容", + "report_reason_violence": "暴力", + "report_reason_spam": "骚扰", + "report_reason_other": "其他", + "profile_reported": "个人资料已举报", + "your_friend_code": "你的好友代码:", + "upload_banner": "上传横幅", + "uploading_banner": "上传横幅中…", + "background_image_updated": "背景图片已更新" + }, + "achievement": { + "achievement_unlocked": "成就已解锁", + "user_achievements": "{{displayName}}的成就", + "your_achievements": "你的成就", + "unlocked_at": "解锁于:", + "subscription_needed": "需要订阅 Hydra Cloud 才能看到此内容", + "new_achievements_unlocked": "从 {{gameCount}} 游戏中解锁 {{achievementCount}} 新成就" + }, + "tour": { + "subscription_tour_title": "Hydra 云订阅", + "subscribe_now": "现在订购", + "cloud_saving": "云存档", + "cloud_achievements": "将你的成就保存至云端", + "animated_profile_picture": "动画头像", + "premium_support": "高级技术支持", + "show_and_compare_achievements": "展示并与其他用户比较您的成就", + "animated_profile_banner": "动态个人简介横幅" } } diff --git a/src/main/entity/user-preferences.entity.ts b/src/main/entity/user-preferences.entity.ts index dc6d465d..b43d463e 100644 --- a/src/main/entity/user-preferences.entity.ts +++ b/src/main/entity/user-preferences.entity.ts @@ -35,6 +35,9 @@ export class UserPreferences { @Column("boolean", { default: false }) runAtStartup: boolean; + @Column("boolean", { default: false }) + startMinimized: boolean; + @CreateDateColumn() createdAt: Date; diff --git a/src/main/events/user-preferences/auto-launch.ts b/src/main/events/user-preferences/auto-launch.ts index 1f4f9cc3..0b1bba21 100644 --- a/src/main/events/user-preferences/auto-launch.ts +++ b/src/main/events/user-preferences/auto-launch.ts @@ -16,15 +16,16 @@ const windowsStartupPath = path.join( const autoLaunch = async ( _event: Electron.IpcMainInvokeEvent, - enabled: boolean + autoLaunchProps: { enabled: boolean; minimized: boolean } ) => { if (!app.isPackaged) return; const appLauncher = new AutoLaunch({ name: app.getName(), + isHidden: autoLaunchProps.minimized, }); - if (enabled) { + if (autoLaunchProps.enabled) { appLauncher.enable().catch((err) => { logger.error(err); }); diff --git a/src/main/index.ts b/src/main/index.ts index 1b1629ef..7f35c317 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -101,7 +101,10 @@ app.whenReady().then(async () => { i18n.changeLanguage(userPreferences.language); } - WindowManager.createMainWindow(); + if (!process.argv.includes("--hidden")) { + WindowManager.createMainWindow(); + } + WindowManager.createNotificationWindow(); WindowManager.createSystemTray(userPreferences?.language || "en"); }); diff --git a/src/main/knex-client.ts b/src/main/knex-client.ts index 5f81ffbc..eec5b054 100644 --- a/src/main/knex-client.ts +++ b/src/main/knex-client.ts @@ -11,7 +11,7 @@ import { AddAchievementNotificationPreference } from "./migrations/2024101301290 import { CreateUserSubscription } from "./migrations/20241015235142_create_user_subscription"; import { AddBackgroundImageUrl } from "./migrations/20241016100249_add_background_image_url"; import { AddWinePrefixToGame } from "./migrations/20241019081648_add_wine_prefix_to_game"; - +import { AddStartMinimizedColumn } from "./migrations/20241030171454_add_start_minimized_column"; export type HydraMigration = Knex.Migration & { name: string }; class MigrationSource implements Knex.MigrationSource { @@ -27,6 +27,7 @@ class MigrationSource implements Knex.MigrationSource { CreateUserSubscription, AddBackgroundImageUrl, AddWinePrefixToGame, + AddStartMinimizedColumn, ]); } getMigrationName(migration: HydraMigration): string { diff --git a/src/main/migrations/20241030171454_add_start_minimized_column.ts b/src/main/migrations/20241030171454_add_start_minimized_column.ts new file mode 100644 index 00000000..69ede189 --- /dev/null +++ b/src/main/migrations/20241030171454_add_start_minimized_column.ts @@ -0,0 +1,17 @@ +import type { HydraMigration } from "@main/knex-client"; +import type { Knex } from "knex"; + +export const AddStartMinimizedColumn: HydraMigration = { + name: "AddStartMinimizedColumn", + up: (knex: Knex) => { + return knex.schema.alterTable("user_preferences", (table) => { + return table.boolean("startMinimized").notNullable().defaultTo(0); + }); + }, + + down: async (knex: Knex) => { + return knex.schema.alterTable("user_preferences", (table) => { + return table.dropColumn("startMinimized"); + }); + }, +}; diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index e1dc4dfc..b61c991a 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -310,14 +310,15 @@ export class WindowManager { if (process.platform !== "darwin") { tray.addListener("click", () => { if (this.mainWindow) { - if (WindowManager.mainWindow?.isMinimized()) - WindowManager.mainWindow.restore(); - - WindowManager.mainWindow?.focus(); - return; + if ( + WindowManager.mainWindow?.isMinimized() || + !WindowManager.mainWindow?.isVisible() + ) { + WindowManager.mainWindow?.show(); + } + } else { + this.createMainWindow(); } - - this.createMainWindow(); }); tray.addListener("right-click", showContextMenu); diff --git a/src/preload/index.ts b/src/preload/index.ts index 90c50763..9baa8325 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -101,7 +101,8 @@ contextBridge.exposeInMainWorld("electron", { getUserPreferences: () => ipcRenderer.invoke("getUserPreferences"), updateUserPreferences: (preferences: UserPreferences) => ipcRenderer.invoke("updateUserPreferences", preferences), - autoLaunch: (enabled: boolean) => ipcRenderer.invoke("autoLaunch", enabled), + autoLaunch: (autoLaunchProps: { enabled: boolean; minimized: boolean }) => + ipcRenderer.invoke("autoLaunch", autoLaunchProps), authenticateRealDebrid: (apiToken: string) => ipcRenderer.invoke("authenticateRealDebrid", apiToken), diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 929b00e9..a12862b3 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -114,7 +114,10 @@ declare global { updateUserPreferences: ( preferences: Partial ) => Promise; - autoLaunch: (enabled: boolean) => Promise; + autoLaunch: (autoLaunchProps: { + enabled: boolean; + minimized: boolean; + }) => Promise; authenticateRealDebrid: (apiToken: string) => Promise; /* Download sources */ diff --git a/src/renderer/src/pages/settings/settings-behavior.tsx b/src/renderer/src/pages/settings/settings-behavior.tsx index f0b4b4c4..4e3ef2f3 100644 --- a/src/renderer/src/pages/settings/settings-behavior.tsx +++ b/src/renderer/src/pages/settings/settings-behavior.tsx @@ -17,6 +17,7 @@ export function SettingsBehavior() { const [form, setForm] = useState({ preferQuitInsteadOfHiding: false, runAtStartup: false, + startMinimized: false, }); const { t } = useTranslation("settings"); @@ -26,6 +27,7 @@ export function SettingsBehavior() { setForm({ preferQuitInsteadOfHiding: userPreferences.preferQuitInsteadOfHiding, runAtStartup: userPreferences.runAtStartup, + startMinimized: userPreferences.startMinimized, }); } }, [userPreferences]); @@ -58,11 +60,32 @@ export function SettingsBehavior() { label={t("launch_with_system")} onChange={() => { handleChange({ runAtStartup: !form.runAtStartup }); - window.electron.autoLaunch(!form.runAtStartup); + window.electron.autoLaunch({ + enabled: !form.runAtStartup, + minimized: form.startMinimized, + }); }} checked={form.runAtStartup} /> )} + + {showRunAtStartup && ( +
+ { + handleChange({ startMinimized: !form.startMinimized }); + window.electron.autoLaunch({ + minimized: !form.startMinimized, + enabled: form.runAtStartup, + }); + }} + /> +
+ )} ); } diff --git a/src/types/index.ts b/src/types/index.ts index 7f970d63..9bb25e3f 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -160,6 +160,7 @@ export interface UserPreferences { realDebridApiToken: string | null; preferQuitInsteadOfHiding: boolean; runAtStartup: boolean; + startMinimized: boolean; } export interface Steam250Game {