From edc90d9ebef5812b22e516a5e43f5718c1819d42 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Fri, 21 Jun 2024 21:29:25 -0300 Subject: [PATCH 1/6] feat: add ru and uk updated string --- src/locales/ru/translation.json | 56 +++++++-------- src/locales/uk/translation.json | 122 +++++++++++++++++++++++++++++--- 2 files changed, 142 insertions(+), 36 deletions(-) diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 5019de5ea..163808ec8 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -1,6 +1,6 @@ { "app": { - "successfully_signed_in": "Successfully signed in (TRANSLATE ME)" + "successfully_signed_in": "Успешный вход в систему" }, "home": { "featured": "Рекомендованное", @@ -20,7 +20,7 @@ "home": "Главная", "queued": "{{title}} (В очереди)", "game_has_no_executable": "Файл запуска игры не выбран", - "sign_in": "Sign in (TRANSLATE ME)" + "sign_in": "Войти" }, "header": { "search": "Поиск", @@ -183,12 +183,12 @@ "sync_download_sources": "Синхронизировать источники", "removed_download_source": "Источник загрузок удален", "added_download_source": "Источник загрузок добавлен", - "download_sources_synced": "All download sources are synced (TRANSLATE ME)", - "insert_valid_json_url": "Insert a valid JSON url (TRANSLATE ME)", - "found_download_option_zero": "No download option found (TRANSLATE ME)", - "found_download_option_one": "Found {{countFormatted}} download option (TRANSLATE ME)", - "found_download_option_other": "Found {{countFormatted}} download options (TRANSLATE ME)", - "import": "Import (TRANSLATE ME)" + "download_sources_synced": "Все источники для загрузки синхронизировано", + "insert_valid_json_url": "Вставьте действительный URL JSON-файла", + "found_download_option_zero": "Не найдено вариантов загрузки", + "found_download_option_one": "Найдено {{countFormatted}} вариант загрузки", + "found_download_option_other": "Найдено {{countFormatted}} вариантов загрузки", + "import": "Импортировать" }, "notifications": { "download_complete": "Загрузка завершена", @@ -216,25 +216,25 @@ "toggle_password_visibility": "Показывать пароль" }, "user_profile": { - "amount_hours": "{{amount}} hours (TRANSLATE ME)", - "amount_minutes": "{{amount}} minutes (TRANSLATE ME)", - "last_time_played": "Last played {{period}} (TRANSLATE ME)", - "activity": "Recent activity (TRANSLATE ME)", - "library": "Library (TRANSLATE ME)", - "total_play_time": "Total playtime: {{amount}} (TRANSLATE ME)", - "no_recent_activity_title": "Hmmm… nothing here (TRANSLATE ME)", - "no_recent_activity_description": "You haven't played any games recently. It's time to change that! (TRANSLATE ME)", - "display_name": "Display name (TRANSLATE ME)", - "saving": "Saving (TRANSLATE ME)", - "save": "Save (TRANSLATE ME)", - "edit_profile": "Edit Profile (TRANSLATE ME)", - "saved_successfully": "Saved successfully (TRANSLATE ME)", - "try_again": "Please, try again (TRANSLATE ME)", - "sign_out_modal_title": "Are you sure? (TRANSLATE ME)", - "cancel": "Cancel (TRANSLATE ME)", - "successfully_signed_out": "Successfully signed out (TRANSLATE ME)", - "sign_out": "Sign out (TRANSLATE ME)", - "playing_for": "Playing for {{amount}} (TRANSLATE ME)", - "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? (TRANSLATE ME)" + "amount_hours": "{{amount}} часов", + "amount_minutes": "{{amount}} минут", + "last_time_played": "Последняя игра {{period}}", + "activity": "Недавняя активность", + "library": "Библиотека", + "total_play_time": "Всего наиграно: {{amount}}", + "no_recent_activity_title": "Хммм... Тут ничего нет", + "no_recent_activity_description": "Вы давно не играли в игры. Пора это изменить!", + "display_name": "Отображаемое Имя", + "saving": "Сохранение", + "save": "Сохранено", + "edit_profile": "Редактировать Профиль", + "saved_successfully": "Успешно сохранено", + "try_again": "Пожалуйста, попробуйте ещё раз", + "sign_out_modal_title": "Вы уверены?", + "cancel": "Отменить", + "successfully_signed_out": "Успешный выход из аккаунта", + "sign_out": "Выйти", + "playing_for": "Сыграно {{amount}}", + "sign_out_modal_text": "Ваша библиотека связана с текущей учетной записью. При выходе из системы ваша библиотека станет недоступна, и прогресс не будет сохранен. Продолжить выход?" } } diff --git a/src/locales/uk/translation.json b/src/locales/uk/translation.json index 9d62abb3d..48dec3e4c 100644 --- a/src/locales/uk/translation.json +++ b/src/locales/uk/translation.json @@ -1,4 +1,7 @@ { + "app": { + "successfully_signed_in": "Успішний вхід в систему" + }, "home": { "featured": "Рекомендоване", "trending": "У тренді", @@ -14,7 +17,10 @@ "paused": "{{title}} (Призупинено)", "downloading": "{{title}} ({{percentage}} - Завантаження…)", "filter": "Фільтр бібліотеки", - "home": "Головна" + "home": "Головна", + "game_has_no_executable": "Не було вибрано файл для запуску гри", + "queued": "{{title}} в черзі", + "sign_in": "Увійти" }, "header": { "search": "Пошук", @@ -22,12 +28,15 @@ "catalogue": "Каталог", "downloads": "Завантаження", "search_results": "Результати пошуку", - "settings": "Налаштування" + "settings": "Налаштування", + "version_available_download": "Доступна версія {{version}}. Натисніть тут, щоб перезапустити та встановити.", + "version_available_install": "Доступна версія {{version}}. Натисніть тут для завантаження." }, "bottom_panel": { "no_downloads_in_progress": "Немає активних завантажень", "downloading_metadata": "Завантаження метаданих {{title}}…", - "downloading": "Завантаження {{title}}… ({{percentage}} завершено) - Закінчення {{eta}} - {{speed}}" + "downloading": "Завантаження {{title}}… ({{percentage}} завершено) - Закінчення {{eta}} - {{speed}}", + "calculating_eta": "Завантаження {{title}}… ({{percentage}} завершено) - Обчислення залишкового часу…" }, "catalogue": { "next_page": "Наступна сторінка", @@ -72,13 +81,42 @@ "change": "Змінити", "repacks_modal_description": "Виберіть репак, який хочете завантажити", "select_folder_hint": "Щоб змінити теку за замовчуванням, відкрийте", - "download_now": "Завантажити зараз" + "download_now": "Завантажити зараз", + "calculating_eta": "Обчислення залишкового часу…", + "create_shortcut": "Створити ярлик на робочому столі", + "danger_zone_section_description": "Видалити цю гру з вашої бібліотеки або файли скачані Hydra", + "danger_zone_section_title": "Небезпечна зона", + "download_in_progress": "Триває завантаження.", + "download_options": "Варіантів завантаження", + "download_path": "Тека для завантажень", + "download_paused": "Завантаження призупинено", + "download_settings": "Налаштування завантаження", + "downloader": "Завантажувач", + "downloads_secion_title": "Завантаження", + "downloads_section_description": "Перевірити наявність оновлень або інших версій гри", + "executable_section_description": "Шлях до файлу, який буде запущений при натисканні на кнопку \"Play\"", + "executable_section_title": "Файл", + "last_downloaded_option": "Останній варіант завантаження", + "next_screenshot": "Наступний скрішнот", + "no_executable_selected": "Файл не вибрано", + "no_shop_details": "Не вдалося отримати опис", + "open_download_location": "Переглянути папку завантажень", + "open_folder": "Відкрити папку", + "open_screenshot": "Відкрити скріншот", + "options": "Налаштування", + "paused": "Призупинено", + "previous_screenshot": "Попередній скріншот", + "remove_files": "Видалити файли", + "remove_from_library_description": "{{game}} буде видалено з вашої бібліотеки", + "remove_from_library_title": "Ви впевнені?", + "screenshot": "Скріншот", + "select_executable": "Обрати" }, "activation": { "title": "Активувати Hydra", "installation_id": "ID установки:", "enter_activation_code": "Введіть ваш активаційний код", - "message": "Якщо ви не знаєте, де його запросити, то не повинні мати цього.", + "message": "Якщо ви не знаєте, де його запросити, то не повинні мати його.", "activate": "Активувати", "loading": "Завантаження…" }, @@ -97,7 +135,14 @@ "delete": "Видалити інсталятор", "delete_modal_title": "Ви впевнені?", "delete_modal_description": "Це видалить усі інсталяційні файли з вашого комп'ютера", - "install": "Встановити" + "install": "Встановити", + "download_in_progress": "В процесі", + "downloads_completed": "Завершено", + "no_downloads_description": "Ви ще нічого не завантажили через Hydra, але ніколи не пізно почати.", + "no_downloads_title": "Тут так пусто...", + "queued": "В черзі", + "queued_downloads": "Завантаження в черзі", + "removed": "Не завантажено" }, "settings": { "downloads_path": "Тека завантажень", @@ -106,8 +151,44 @@ "enable_download_notifications": "Після завершення завантаження", "enable_repack_list_notifications": "Коли додається новий репак", "behavior": "Поведінка", - "quit_app_instead_hiding": "Закривати програму замість того, щоб згортати її в трей", - "launch_with_system": "Запускати програми із запуском комп'ютера" + "quit_app_instead_hiding": "Закривати Hydra замість того, щоб згортати її в трей", + "launch_with_system": "Запускати Hydra із запуском комп'ютера", + "add_download_source": "Добавити джерело", + "add_download_source_description": "Введіть посилання на .json-файл", + "added_download_source": "Джерело для завантаження було додано", + "changes_saved": "Зміни успішно збережено", + "download_count_one": "{{countFormatted}} завантаження в списку", + "download_count_other": "{{countFormatted}} завантажень в списку", + "download_count_zero": "В списку немає завантажень", + "download_options_one": "{{countFormatted}} доступний варіант завантаження", + "download_options_other": "{{countFormatted}} доступних варіантів завантаження", + "download_options_zero": "Немає доступних завантажень", + "download_source_errored": "Помилка", + "download_source_up_to_date": "Оновлено", + "download_source_url": "Посилання на джерело", + "download_sources": "Джерела для завантаження", + "download_sources_description": "Hydra буде отримувати посилання для завантажень із цих джерел. URL має містити пряме посилання на .json-файл із посиланнями для завантажень.", + "download_sources_synced": "Всі джерела для завантаження синхронізовано", + "enable_real_debrid": "Включити Real-Debrid", + "found_download_option_one": "Знайдено {{countFormatted}} варіант завантаження", + "found_download_option_other": "Знайдено {{countFormatted}} варіантів завантаження", + "found_download_option_zero": "Немає доступних завантажень", + "general": "Основні", + "import": "Імпортувати", + "insert_valid_json_url": "Вставте дійсний URL JSON-файлу", + "language": "Мова", + "real_debrid_api_token": "API-токен", + "real_debrid_api_token_hint": "API токен можливо отримати <0>тут", + "real_debrid_api_token_label": "Real-Debrid API-токен", + "real_debrid_description": "Real-Debrid — це необмежений завантажувач, який дозволяє швидко завантажувати файли, розміщені в Інтернеті, або миттєво передавати їх у плеєр через приватну мережу, що дозволяє обходити будь-які блокування.", + "real_debrid_free_account_error": "Акаунт \"{{username}}\" - не має наявної підписки. Будь ласка, оформіть підписку на Real-Debrid", + "real_debrid_invalid_token": "Невірний API-токен", + "real_debrid_linked_message": "Акаунт \"{{username}}\" привязаний", + "remove_download_source": "Видалити", + "removed_download_source": "Джерело завантажень було видалено", + "save_changes": "Зберегти зміни", + "sync_download_sources": "Синхронізувати джерела", + "validate_download_source": "Перевірити" }, "notifications": { "download_complete": "Завантаження завершено", @@ -130,5 +211,30 @@ }, "modal": { "close": "Закрити" + }, + "forms": { + "toggle_password_visibility": "Показувати пароль" + }, + "user_profile": { + "activity": "Остання активність", + "amount_hours": "{{amount}} годин", + "amount_minutes": "{{amount}} хвилин", + "cancel": "Скасувати", + "display_name": "Відображуване ім'я", + "edit_profile": "Редагувати профіль", + "last_time_played": "Остання гра {{period}}", + "library": "Бібліотека", + "no_recent_activity_description": "Ви давно не грали в ігри. Пора це змінити!", + "no_recent_activity_title": "Хммм... Тут нічого немає", + "playing_for": "Зіграно {{amount}}", + "save": "Збережено", + "saved_successfully": "Успішно збережено", + "saving": "Збереження", + "sign_out": "Вийти", + "sign_out_modal_text": "Ваша бібліотека пов'язана з поточним обліковим записом. При виході з системи ваша бібліотека буде недоступною, і прогрес не буде збережено. Продовжити вихід?", + "sign_out_modal_title": "Ви впевнені?", + "successfully_signed_out": "Успішний вихід з акаунту", + "total_play_time": "Всього зіграно: {{amount}}", + "try_again": "Будь ласка, попробуйте ще раз" } } From 37b9eca0e152e8cc0c1108cde0b128ebab2b81b9 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 22 Jun 2024 01:14:02 -0300 Subject: [PATCH 2/6] feat: show toast after create shortcut --- src/locales/en/translation.json | 4 +++- src/locales/pt/translation.json | 4 +++- .../pages/game-details/modals/game-options-modal.tsx | 12 ++++++++++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index b13a5da0c..abe9246c1 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -119,7 +119,9 @@ "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" + "last_downloaded_option": "Last downloaded option", + "create_shortcut_success": "Shortcut created successfully", + "create_shortcut_error": "Error creating shortcut" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index 677321448..c5fe70251 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -116,7 +116,9 @@ "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" + "last_downloaded_option": "Última opção baixada", + "create_shortcut_success": "Atalho criado com sucesso", + "create_shortcut_error": "Erro ao criar atalho" }, "activation": { "title": "Ativação", diff --git a/src/renderer/src/pages/game-details/modals/game-options-modal.tsx b/src/renderer/src/pages/game-details/modals/game-options-modal.tsx index c03b96865..e887dd922 100644 --- a/src/renderer/src/pages/game-details/modals/game-options-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/game-options-modal.tsx @@ -5,7 +5,7 @@ import type { Game } from "@types"; import * as styles from "./game-options-modal.css"; import { gameDetailsContext } from "@renderer/context"; import { DeleteGameModal } from "@renderer/pages/downloads/delete-game-modal"; -import { useDownload } from "@renderer/hooks"; +import { useDownload, useToast } from "@renderer/hooks"; import { RemoveGameFromLibraryModal } from "./remove-from-library-modal"; export interface GameOptionsModalProps { @@ -21,6 +21,8 @@ export function GameOptionsModal({ }: GameOptionsModalProps) { const { t } = useTranslation("game_details"); + const { showSuccessToast, showErrorToast } = useToast(); + const { updateGame, setShowRepacksModal, selectGameExecutable } = useContext(gameDetailsContext); @@ -61,7 +63,13 @@ export function GameOptionsModal({ }; const handleCreateShortcut = async () => { - await window.electron.createGameShortcut(game.id); + window.electron.createGameShortcut(game.id).then((success) => { + if (success) { + showSuccessToast(t("create_shortcut_success")); + } else { + showErrorToast(t("create_shortcut_error")); + } + }); }; const handleOpenDownloadFolder = async () => { From be5f045a0660d13d13665a618bf0d9cf5585d3c1 Mon Sep 17 00:00:00 2001 From: JackEnx Date: Sat, 22 Jun 2024 14:25:43 -0300 Subject: [PATCH 3/6] refactor: remove gif and bmp profile image mime --- src/renderer/src/pages/user/user-edit-modal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/pages/user/user-edit-modal.tsx b/src/renderer/src/pages/user/user-edit-modal.tsx index d61a2da5d..a22650eeb 100644 --- a/src/renderer/src/pages/user/user-edit-modal.tsx +++ b/src/renderer/src/pages/user/user-edit-modal.tsx @@ -40,7 +40,7 @@ export const UserEditProfileModal = ({ filters: [ { name: "Image", - extensions: ["jpg", "jpeg", "png", "gif", "webp", "bmp"], + extensions: ["jpg", "jpeg", "png", "webp"], }, ], }); From 6d3b04fc3c58a1de50546a3ce87a296d60b14795 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Sat, 22 Jun 2024 20:15:07 +0100 Subject: [PATCH 4/6] feat: adding deep linking to import sources --- src/main/events/helpers/validators.ts | 1 - src/main/index.ts | 23 +++-- src/main/services/window-manager.ts | 10 +- src/renderer/src/context/index.ts | 1 + .../src/context/settings/settings.context.tsx | 73 ++++++++++++++ .../settings/add-download-source-modal.tsx | 30 +++--- .../src/pages/settings/settings-behavior.tsx | 15 +-- .../settings/settings-download-sources.tsx | 17 +++- .../src/pages/settings/settings-general.tsx | 15 ++- .../pages/settings/settings-real-debrid.tsx | 15 ++- src/renderer/src/pages/settings/settings.tsx | 98 +++++++++---------- 11 files changed, 191 insertions(+), 107 deletions(-) create mode 100644 src/renderer/src/context/settings/settings.context.tsx diff --git a/src/main/events/helpers/validators.ts b/src/main/events/helpers/validators.ts index f3c9d8442..ee36bb85e 100644 --- a/src/main/events/helpers/validators.ts +++ b/src/main/events/helpers/validators.ts @@ -5,7 +5,6 @@ export const downloadSourceSchema = z.object({ downloads: z.array( z.object({ title: z.string().max(255), - downloaders: z.array(z.enum(["real_debrid", "torrent"])), uris: z.array(z.string()), uploadDate: z.string().max(255), fileSize: z.string().max(255), diff --git a/src/main/index.ts b/src/main/index.ts index b2d072752..837db5f54 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -73,7 +73,16 @@ app.on("browser-window-created", (_, window) => { optimizer.watchWindowShortcuts(window); }); -app.on("second-instance", (_event) => { +const handleDeepLinkPath = (uri?: string) => { + if (!uri) return; + const url = new URL(uri); + + if (url.host === "install-source") { + WindowManager.redirect(`settings${url.search}`); + } +}; + +app.on("second-instance", (_event, commandLine) => { // Someone tried to run a second instance, we should focus our window. if (WindowManager.mainWindow) { if (WindowManager.mainWindow.isMinimized()) @@ -84,16 +93,12 @@ app.on("second-instance", (_event) => { WindowManager.createMainWindow(); } - // const [, path] = commandLine.pop()?.split("://") ?? []; - // if (path) { - // WindowManager.redirect(path); - // } + handleDeepLinkPath(commandLine.pop()); }); -// app.on("open-url", (_event, url) => { -// const [, path] = url.split("://"); -// WindowManager.redirect(path); -// }); +app.on("open-url", (_event, url) => { + handleDeepLinkPath(url); +}); // Quit when all windows are closed, except on macOS. There, it's common // for applications and their menu bar to stay active until the user quits diff --git a/src/main/services/window-manager.ts b/src/main/services/window-manager.ts index 3e3eaf1c6..9f4d821e1 100644 --- a/src/main/services/window-manager.ts +++ b/src/main/services/window-manager.ts @@ -9,7 +9,7 @@ import { shell, } from "electron"; import { is } from "@electron-toolkit/utils"; -import { t } from "i18next"; +import i18next, { t } from "i18next"; import path from "node:path"; import icon from "@resources/icon.png?asset"; import trayIcon from "@resources/tray-icon.png?asset"; @@ -100,7 +100,13 @@ export class WindowManager { authWindow.removeMenu(); - authWindow.loadURL("https://auth.hydra.losbroxas.org/"); + const searchParams = new URLSearchParams({ + lng: i18next.language, + }); + + authWindow.loadURL( + `https://auth.hydra.losbroxas.org/?${searchParams.toString()}` + ); authWindow.once("ready-to-show", () => { authWindow.show(); diff --git a/src/renderer/src/context/index.ts b/src/renderer/src/context/index.ts index cecf58731..ded6dc3e2 100644 --- a/src/renderer/src/context/index.ts +++ b/src/renderer/src/context/index.ts @@ -1 +1,2 @@ export * from "./game-details/game-details.context"; +export * from "./settings/settings.context"; diff --git a/src/renderer/src/context/settings/settings.context.tsx b/src/renderer/src/context/settings/settings.context.tsx new file mode 100644 index 000000000..dff006fce --- /dev/null +++ b/src/renderer/src/context/settings/settings.context.tsx @@ -0,0 +1,73 @@ +import { createContext, useEffect, useState } from "react"; + +import { setUserPreferences } from "@renderer/features"; +import { useAppDispatch } from "@renderer/hooks"; +import type { UserPreferences } from "@types"; +import { useSearchParams } from "react-router-dom"; + +export interface SettingsContext { + updateUserPreferences: (values: Partial) => Promise; + setCurrentCategoryIndex: React.Dispatch>; + clearSourceUrl: () => void; + sourceUrl: string | null; + currentCategoryIndex: number; +} + +export const settingsContext = createContext({ + updateUserPreferences: async () => {}, + setCurrentCategoryIndex: () => {}, + clearSourceUrl: () => {}, + sourceUrl: null, + currentCategoryIndex: 0, +}); + +const { Provider } = settingsContext; +export const { Consumer: SettingsContextConsumer } = settingsContext; + +export interface SettingsContextProviderProps { + children: React.ReactNode; +} + +export function SettingsContextProvider({ + children, +}: SettingsContextProviderProps) { + const dispatch = useAppDispatch(); + const [sourceUrl, setSourceUrl] = useState(null); + const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0); + + const [searchParams] = useSearchParams(); + const defaultSourceUrl = searchParams.get("urls"); + + useEffect(() => { + if (sourceUrl) setCurrentCategoryIndex(2); + }, [sourceUrl]); + + useEffect(() => { + if (defaultSourceUrl) { + setSourceUrl(defaultSourceUrl); + } + }, [defaultSourceUrl]); + + const clearSourceUrl = () => setSourceUrl(null); + + const updateUserPreferences = async (values: Partial) => { + await window.electron.updateUserPreferences(values); + window.electron.getUserPreferences().then((userPreferences) => { + dispatch(setUserPreferences(userPreferences)); + }); + }; + + return ( + + {children} + + ); +} diff --git a/src/renderer/src/pages/settings/add-download-source-modal.tsx b/src/renderer/src/pages/settings/add-download-source-modal.tsx index 10e497dd4..98495c67f 100644 --- a/src/renderer/src/pages/settings/add-download-source-modal.tsx +++ b/src/renderer/src/pages/settings/add-download-source-modal.tsx @@ -1,8 +1,9 @@ -import { useEffect, useState } from "react"; +import { useCallback, useContext, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Button, Modal, TextField } from "@renderer/components"; import { SPACING_UNIT } from "@renderer/theme.css"; +import { settingsContext } from "@renderer/context"; interface AddDownloadSourceModalProps { visible: boolean; @@ -23,24 +24,31 @@ export function AddDownloadSourceModal({ downloadCount: number; } | null>(null); - useEffect(() => { - setValue(""); - setIsLoading(false); - setValidationResult(null); - }, [visible]); - const { t } = useTranslation("settings"); - const handleValidateDownloadSource = async () => { + const { sourceUrl } = useContext(settingsContext); + + const handleValidateDownloadSource = useCallback(async (url: string) => { setIsLoading(true); try { - const result = await window.electron.validateDownloadSource(value); + const result = await window.electron.validateDownloadSource(url); setValidationResult(result); } finally { setIsLoading(false); } - }; + }, []); + + useEffect(() => { + setValue(""); + setIsLoading(false); + setValidationResult(null); + + if (sourceUrl) { + setValue(sourceUrl); + handleValidateDownloadSource(sourceUrl); + } + }, [visible, handleValidateDownloadSource, sourceUrl]); const handleAddDownloadSource = async () => { await window.electron.addDownloadSource(value); @@ -73,7 +81,7 @@ export function AddDownloadSourceModal({ type="button" theme="outline" style={{ alignSelf: "flex-end" }} - onClick={handleValidateDownloadSource} + onClick={() => handleValidateDownloadSource(value)} disabled={isLoading || !value} > {t("validate_download_source")} diff --git a/src/renderer/src/pages/settings/settings-behavior.tsx b/src/renderer/src/pages/settings/settings-behavior.tsx index 4f8245a65..7f7aa5fd0 100644 --- a/src/renderer/src/pages/settings/settings-behavior.tsx +++ b/src/renderer/src/pages/settings/settings-behavior.tsx @@ -1,22 +1,17 @@ -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; -import type { UserPreferences } from "@types"; - import { CheckboxField } from "@renderer/components"; import { useAppSelector } from "@renderer/hooks"; +import { settingsContext } from "@renderer/context"; -export interface SettingsBehaviorProps { - updateUserPreferences: (values: Partial) => void; -} - -export function SettingsBehavior({ - updateUserPreferences, -}: SettingsBehaviorProps) { +export function SettingsBehavior() { const userPreferences = useAppSelector( (state) => state.userPreferences.value ); + const { updateUserPreferences } = useContext(settingsContext); + const [form, setForm] = useState({ preferQuitInsteadOfHiding: false, runAtStartup: false, diff --git a/src/renderer/src/pages/settings/settings-download-sources.tsx b/src/renderer/src/pages/settings/settings-download-sources.tsx index 6e12949d1..8214117a3 100644 --- a/src/renderer/src/pages/settings/settings-download-sources.tsx +++ b/src/renderer/src/pages/settings/settings-download-sources.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { TextField, Button, Badge } from "@renderer/components"; import { useTranslation } from "react-i18next"; @@ -10,6 +10,7 @@ import { AddDownloadSourceModal } from "./add-download-source-modal"; import { useToast } from "@renderer/hooks"; import { DownloadSourceStatus } from "@shared"; import { SPACING_UNIT } from "@renderer/theme.css"; +import { settingsContext } from "@renderer/context"; export function SettingsDownloadSources() { const [showAddDownloadSourceModal, setShowAddDownloadSourceModal] = @@ -18,8 +19,9 @@ export function SettingsDownloadSources() { const [isSyncingDownloadSources, setIsSyncingDownloadSources] = useState(false); - const { t } = useTranslation("settings"); + const { sourceUrl, clearSourceUrl } = useContext(settingsContext); + const { t } = useTranslation("settings"); const { showSuccessToast } = useToast(); const getDownloadSources = async () => { @@ -32,6 +34,10 @@ export function SettingsDownloadSources() { getDownloadSources(); }, []); + useEffect(() => { + if (sourceUrl) setShowAddDownloadSourceModal(true); + }, [sourceUrl]); + const handleRemoveSource = async (id: number) => { await window.electron.removeDownloadSource(id); showSuccessToast(t("removed_download_source")); @@ -63,11 +69,16 @@ export function SettingsDownloadSources() { [DownloadSourceStatus.Errored]: t("download_source_errored"), }; + const handleModalClose = () => { + clearSourceUrl(); + setShowAddDownloadSourceModal(false); + }; + return ( <> setShowAddDownloadSourceModal(false)} + onClose={handleModalClose} onAddDownloadSource={handleAddDownloadSource} /> diff --git a/src/renderer/src/pages/settings/settings-general.tsx b/src/renderer/src/pages/settings/settings-general.tsx index fb9e459ea..e09ebb114 100644 --- a/src/renderer/src/pages/settings/settings-general.tsx +++ b/src/renderer/src/pages/settings/settings-general.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import ISO6391 from "iso-639-1"; import { @@ -8,27 +8,24 @@ import { SelectField, } from "@renderer/components"; import { useTranslation } from "react-i18next"; -import type { UserPreferences } from "@types"; + import { useAppSelector } from "@renderer/hooks"; import { changeLanguage } from "i18next"; import * as languageResources from "@locales"; import { orderBy } from "lodash-es"; +import { settingsContext } from "@renderer/context"; interface LanguageOption { option: string; nativeName: string; } -export interface SettingsGeneralProps { - updateUserPreferences: (values: Partial) => void; -} - -export function SettingsGeneral({ - updateUserPreferences, -}: SettingsGeneralProps) { +export function SettingsGeneral() { const { t } = useTranslation("settings"); + const { updateUserPreferences } = useContext(settingsContext); + const userPreferences = useAppSelector( (state) => state.userPreferences.value ); diff --git a/src/renderer/src/pages/settings/settings-real-debrid.tsx b/src/renderer/src/pages/settings/settings-real-debrid.tsx index 3dc4186d4..35804664e 100644 --- a/src/renderer/src/pages/settings/settings-real-debrid.tsx +++ b/src/renderer/src/pages/settings/settings-real-debrid.tsx @@ -1,26 +1,23 @@ -import { useEffect, useState } from "react"; +import { useContext, useEffect, useState } from "react"; import { Trans, useTranslation } from "react-i18next"; import { Button, CheckboxField, Link, TextField } from "@renderer/components"; import * as styles from "./settings-real-debrid.css"; -import type { UserPreferences } from "@types"; + import { useAppSelector, useToast } from "@renderer/hooks"; import { SPACING_UNIT } from "@renderer/theme.css"; +import { settingsContext } from "@renderer/context"; const REAL_DEBRID_API_TOKEN_URL = "https://real-debrid.com/apitoken"; -export interface SettingsRealDebridProps { - updateUserPreferences: (values: Partial) => void; -} - -export function SettingsRealDebrid({ - updateUserPreferences, -}: SettingsRealDebridProps) { +export function SettingsRealDebrid() { const userPreferences = useAppSelector( (state) => state.userPreferences.value ); + const { updateUserPreferences } = useContext(settingsContext); + const [isLoading, setIsLoading] = useState(false); const [form, setForm] = useState({ useRealDebrid: false, diff --git a/src/renderer/src/pages/settings/settings.tsx b/src/renderer/src/pages/settings/settings.tsx index c4b5964b8..2718470f2 100644 --- a/src/renderer/src/pages/settings/settings.tsx +++ b/src/renderer/src/pages/settings/settings.tsx @@ -1,21 +1,20 @@ -import { useState } from "react"; import { Button } from "@renderer/components"; import * as styles from "./settings.css"; import { useTranslation } from "react-i18next"; -import { UserPreferences } from "@types"; import { SettingsRealDebrid } from "./settings-real-debrid"; import { SettingsGeneral } from "./settings-general"; import { SettingsBehavior } from "./settings-behavior"; -import { useAppDispatch } from "@renderer/hooks"; -import { setUserPreferences } from "@renderer/features"; + import { SettingsDownloadSources } from "./settings-download-sources"; +import { + SettingsContextConsumer, + SettingsContextProvider, +} from "@renderer/context"; export function Settings() { const { t } = useTranslation("settings"); - const dispatch = useAppDispatch(); - const categories = [ t("general"), t("behavior"), @@ -23,57 +22,50 @@ export function Settings() { "Real-Debrid", ]; - const [currentCategoryIndex, setCurrentCategoryIndex] = useState(0); - - const handleUpdateUserPreferences = async ( - values: Partial - ) => { - await window.electron.updateUserPreferences(values); - window.electron.getUserPreferences().then((userPreferences) => { - dispatch(setUserPreferences(userPreferences)); - }); - }; - - const renderCategory = () => { - if (currentCategoryIndex === 0) { - return ( - - ); - } + return ( + + + {({ currentCategoryIndex, setCurrentCategoryIndex }) => { + const renderCategory = () => { + if (currentCategoryIndex === 0) { + return ; + } - if (currentCategoryIndex === 1) { - return ( - - ); - } + if (currentCategoryIndex === 1) { + return ; + } - if (currentCategoryIndex === 2) { - return ; - } + if (currentCategoryIndex === 2) { + return ; + } - return ( - - ); - }; + return ; + }; - return ( -
-
-
- {categories.map((category, index) => ( - - ))} -
+ return ( +
+
+
+ {categories.map((category, index) => ( + + ))} +
-

{categories[currentCategoryIndex]}

- {renderCategory()} -
-
+

{categories[currentCategoryIndex]}

+ {renderCategory()} +
+
+ ); + }} +
+
); } From f219634b783f63c15f091f875d6720064126b320 Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Sat, 22 Jun 2024 16:25:12 -0300 Subject: [PATCH 5/6] make toast show over everything --- src/renderer/src/theme.css.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/theme.css.ts b/src/renderer/src/theme.css.ts index 6b5206142..4316ecd04 100644 --- a/src/renderer/src/theme.css.ts +++ b/src/renderer/src/theme.css.ts @@ -22,7 +22,7 @@ export const vars = createGlobalTheme(":root", { small: "12px", }, zIndex: { - toast: "2", + toast: "5", bottomPanel: "3", titleBar: "4", backdrop: "4", From 2e66e88609faf425809ab97c4811b5531f8bf861 Mon Sep 17 00:00:00 2001 From: Antecess <59663394+xxDRV@users.noreply.github.com> Date: Sun, 23 Jun 2024 00:29:18 +0500 Subject: [PATCH 6/6] Improved RU translation + Translated new strings --- src/locales/ru/translation.json | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 163808ec8..058234a70 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -1,6 +1,6 @@ { "app": { - "successfully_signed_in": "Успешный вход в систему" + "successfully_signed_in": "Успешный вход" }, "home": { "featured": "Рекомендованное", @@ -110,7 +110,9 @@ "danger_zone_section_description": "Удалить эту игру из вашей библиотеки или файлы скачанные Hydra", "download_in_progress": "Идёт загрузка", "download_paused": "Загрузка приостановлена", - "last_downloaded_option": "Последний вариант загрузки" + "last_downloaded_option": "Последний вариант загрузки", + "create_shortcut_success": "Ярлык создан", + "create_shortcut_error": "Не удалось создать ярлык" }, "activation": { "title": "Активировать Hydra", @@ -183,10 +185,10 @@ "sync_download_sources": "Синхронизировать источники", "removed_download_source": "Источник загрузок удален", "added_download_source": "Источник загрузок добавлен", - "download_sources_synced": "Все источники для загрузки синхронизировано", + "download_sources_synced": "Все источники загрузок синхронизированы", "insert_valid_json_url": "Вставьте действительный URL JSON-файла", "found_download_option_zero": "Не найдено вариантов загрузки", - "found_download_option_one": "Найдено {{countFormatted}} вариант загрузки", + "found_download_option_one": "Найден {{countFormatted}} вариант загрузки", "found_download_option_other": "Найдено {{countFormatted}} вариантов загрузки", "import": "Импортировать" }, @@ -221,10 +223,10 @@ "last_time_played": "Последняя игра {{period}}", "activity": "Недавняя активность", "library": "Библиотека", - "total_play_time": "Всего наиграно: {{amount}}", + "total_play_time": "Всего сыграно: {{amount}}", "no_recent_activity_title": "Хммм... Тут ничего нет", - "no_recent_activity_description": "Вы давно не играли в игры. Пора это изменить!", - "display_name": "Отображаемое Имя", + "no_recent_activity_description": "Вы давно ни во что не играли. Пора это изменить!", + "display_name": "Отображаемое имя", "saving": "Сохранение", "save": "Сохранено", "edit_profile": "Редактировать Профиль", @@ -235,6 +237,6 @@ "successfully_signed_out": "Успешный выход из аккаунта", "sign_out": "Выйти", "playing_for": "Сыграно {{amount}}", - "sign_out_modal_text": "Ваша библиотека связана с текущей учетной записью. При выходе из системы ваша библиотека станет недоступна, и прогресс не будет сохранен. Продолжить выход?" + "sign_out_modal_text": "Ваша библиотека связана с текущей учетной записью. При выходе из системы ваша библиотека станет недоступна, и прогресс не будет сохранен. Выйти?" } }