mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-19 09:13:57 +00:00
feat: i18n and refactor
This commit is contained in:
@@ -363,7 +363,14 @@
|
||||
"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"
|
||||
"extract_files_by_default": "Extract files by default after download",
|
||||
"achievement_custom_notification_position": "Enable achievements custom notification",
|
||||
"top_left": "Top left",
|
||||
"top_center": "Top center",
|
||||
"top_right": "Top right",
|
||||
"bottom_left": "Bottom left",
|
||||
"bottom_center": "Bottom center",
|
||||
"bottom_right": "Bottom right"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Download complete",
|
||||
@@ -379,7 +386,9 @@
|
||||
"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"
|
||||
"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",
|
||||
|
||||
@@ -349,7 +349,14 @@
|
||||
"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"
|
||||
"extract_files_by_default": "Extrair arquivos automaticamente após o download",
|
||||
"enable_achievement_custom_notifications": "Habilitar notificações de conquistas customizadas",
|
||||
"top_left": "Superior esquerdo",
|
||||
"top_center": "Superior central",
|
||||
"top_right": "Superior direito",
|
||||
"bottom_left": "Inferior esquerdo",
|
||||
"bottom_right": "Inferior direito",
|
||||
"bottom_center": "Inferior central"
|
||||
},
|
||||
"notifications": {
|
||||
"download_complete": "Download concluído",
|
||||
@@ -363,7 +370,9 @@
|
||||
"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"
|
||||
"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",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
AchievementNotificationInfo,
|
||||
Game,
|
||||
GameShop,
|
||||
UnlockedAchievement,
|
||||
@@ -86,7 +87,7 @@ export const mergeAchievements = async (
|
||||
publishNotification &&
|
||||
userPreferences?.achievementNotificationsEnabled
|
||||
) {
|
||||
const achievementsInfo = newAchievements
|
||||
const achievementsInfo: AchievementNotificationInfo[] = newAchievements
|
||||
.toSorted((a, b) => {
|
||||
return a.unlockTime - b.unlockTime;
|
||||
})
|
||||
@@ -101,7 +102,12 @@ export const mergeAchievements = async (
|
||||
.filter((achievement) => Boolean(achievement))
|
||||
.map((achievement) => {
|
||||
return {
|
||||
displayName: achievement!.displayName,
|
||||
title: achievement!.displayName,
|
||||
description: achievement!.description,
|
||||
points: achievement!.points,
|
||||
isHidden: achievement!.hidden,
|
||||
isRare: false,
|
||||
isPlatinum: false,
|
||||
iconUrl: achievement!.icon,
|
||||
};
|
||||
});
|
||||
@@ -109,8 +115,6 @@ export const mergeAchievements = async (
|
||||
if (userPreferences?.achievementCustomNotificationsEnabled !== false) {
|
||||
WindowManager.notificationWindow?.webContents.send(
|
||||
"on-achievement-unlocked",
|
||||
game.objectId,
|
||||
game.shop,
|
||||
userPreferences.achievementCustomNotificationPosition ?? "top_left",
|
||||
achievementsInfo
|
||||
);
|
||||
|
||||
@@ -162,7 +162,7 @@ export const publishExtractionCompleteNotification = async (game: Game) => {
|
||||
};
|
||||
|
||||
export const publishNewAchievementNotification = async (info: {
|
||||
achievements: { displayName: string; iconUrl: string }[];
|
||||
achievements: { title: string; iconUrl: string }[];
|
||||
unlockedAchievementCount: number;
|
||||
totalAchievementCount: number;
|
||||
gameTitle: string;
|
||||
@@ -176,12 +176,12 @@ export const publishNewAchievementNotification = async (info: {
|
||||
gameTitle: info.gameTitle,
|
||||
achievementCount: info.achievements.length,
|
||||
}),
|
||||
body: info.achievements.map((a) => a.displayName).join(", "),
|
||||
body: info.achievements.map((a) => a.title).join(", "),
|
||||
icon: (await downloadImage(info.gameIcon)) ?? icon,
|
||||
}
|
||||
: {
|
||||
title: t("achievement_unlocked", { ns: "achievement" }),
|
||||
body: info.achievements[0].displayName,
|
||||
body: info.achievements[0].title,
|
||||
icon: (await downloadImage(info.achievements[0].iconUrl)) ?? icon,
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import { db, gamesSublevel, levelKeys } from "@main/level";
|
||||
import { orderBy, slice } from "lodash-es";
|
||||
import type {
|
||||
AchievementCustomNotificationPosition,
|
||||
AchievementNotificationInfo,
|
||||
ScreenState,
|
||||
UserPreferences,
|
||||
} from "@types";
|
||||
@@ -371,10 +372,28 @@ export class WindowManager {
|
||||
}
|
||||
|
||||
if (showTestNotification) {
|
||||
const language = userPreferences.language ?? "en";
|
||||
setTimeout(() => {
|
||||
this.notificationWindow?.webContents.send(
|
||||
"on-test-achievement-notification",
|
||||
userPreferences.achievementCustomNotificationPosition ?? "top_left"
|
||||
"on-achievement-unlocked",
|
||||
userPreferences.achievementCustomNotificationPosition ?? "top_left",
|
||||
[
|
||||
{
|
||||
title: t("test_achievement_notification_title", {
|
||||
ns: "notifications",
|
||||
lng: language,
|
||||
}),
|
||||
description: t("test_achievement_notification_description", {
|
||||
ns: "notifications",
|
||||
lng: language,
|
||||
}),
|
||||
iconUrl: "https://cdn.losbroxas.org/favicon.svg",
|
||||
points: 100,
|
||||
isHidden: false,
|
||||
isRare: false,
|
||||
isPlatinum: false,
|
||||
},
|
||||
] as AchievementNotificationInfo[]
|
||||
);
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import type {
|
||||
ShortcutLocation,
|
||||
ShopAssets,
|
||||
AchievementCustomNotificationPosition,
|
||||
AchievementNotificationInfo,
|
||||
} from "@types";
|
||||
import type { AuthPage, CatalogueCategory } from "@shared";
|
||||
import type { AxiosProgressEvent } from "axios";
|
||||
@@ -409,31 +410,15 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
ipcRenderer.invoke("publishNewRepacksNotification", newRepacksCount),
|
||||
onAchievementUnlocked: (
|
||||
cb: (
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
position: AchievementCustomNotificationPosition,
|
||||
achievements?: {
|
||||
displayName: string;
|
||||
iconUrl: string;
|
||||
isHidden: boolean;
|
||||
isRare: boolean;
|
||||
isPlatinum: boolean;
|
||||
}[]
|
||||
position?: AchievementCustomNotificationPosition,
|
||||
achievements?: AchievementNotificationInfo[]
|
||||
) => void
|
||||
) => {
|
||||
const listener = (
|
||||
_event: Electron.IpcRendererEvent,
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
position: AchievementCustomNotificationPosition,
|
||||
achievements?: {
|
||||
displayName: string;
|
||||
iconUrl: string;
|
||||
isHidden: boolean;
|
||||
isRare: boolean;
|
||||
isPlatinum: boolean;
|
||||
}[]
|
||||
) => cb(objectId, shop, position, achievements);
|
||||
position?: AchievementCustomNotificationPosition,
|
||||
achievements?: AchievementNotificationInfo[]
|
||||
) => cb(position, achievements);
|
||||
ipcRenderer.on("on-achievement-unlocked", listener);
|
||||
return () =>
|
||||
ipcRenderer.removeListener("on-achievement-unlocked", listener);
|
||||
@@ -455,17 +440,6 @@ contextBridge.exposeInMainWorld("electron", {
|
||||
return () =>
|
||||
ipcRenderer.removeListener("on-combined-achievements-unlocked", listener);
|
||||
},
|
||||
onTestAchievementNotification: (
|
||||
cb: (position: AchievementCustomNotificationPosition) => void
|
||||
) => {
|
||||
const listener = (
|
||||
_event: Electron.IpcRendererEvent,
|
||||
position: AchievementCustomNotificationPosition
|
||||
) => cb(position);
|
||||
ipcRenderer.on("on-test-achievement-notification", listener);
|
||||
return () =>
|
||||
ipcRenderer.removeListener("on-test-achievement-notification", listener);
|
||||
},
|
||||
updateAchievementCustomNotificationWindow: () =>
|
||||
ipcRenderer.invoke("updateAchievementCustomNotificationWindow"),
|
||||
|
||||
|
||||
16
src/renderer/src/declaration.d.ts
vendored
16
src/renderer/src/declaration.d.ts
vendored
@@ -36,6 +36,7 @@ import type {
|
||||
ShopAssets,
|
||||
ShopDetailsWithAssets,
|
||||
AchievementCustomNotificationPosition,
|
||||
AchievementNotificationInfo,
|
||||
} from "@types";
|
||||
import type { AxiosProgressEvent } from "axios";
|
||||
import type disk from "diskusage";
|
||||
@@ -325,16 +326,8 @@ declare global {
|
||||
publishNewRepacksNotification: (newRepacksCount: number) => Promise<void>;
|
||||
onAchievementUnlocked: (
|
||||
cb: (
|
||||
objectId: string,
|
||||
shop: GameShop,
|
||||
position: AchievementCustomNotificationPosition,
|
||||
achievements?: {
|
||||
displayName: string;
|
||||
iconUrl: string;
|
||||
isHidden: boolean;
|
||||
isRare: boolean;
|
||||
isPlatinum: boolean;
|
||||
}[]
|
||||
position?: AchievementCustomNotificationPosition,
|
||||
achievements?: AchievementNotificationInfo[]
|
||||
) => void
|
||||
) => () => Electron.IpcRenderer;
|
||||
onCombinedAchievementsUnlocked: (
|
||||
@@ -344,9 +337,6 @@ declare global {
|
||||
position: AchievementCustomNotificationPosition
|
||||
) => void
|
||||
) => () => Electron.IpcRenderer;
|
||||
onTestAchievementNotification: (
|
||||
cb: (position: AchievementCustomNotificationPosition) => void
|
||||
) => Electron.IpcRenderer;
|
||||
updateAchievementCustomNotificationWindow: () => Promise<void>;
|
||||
|
||||
/* Themes */
|
||||
|
||||
@@ -3,14 +3,12 @@ import achievementSound from "@renderer/assets/audio/achievement.wav";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import cn from "classnames";
|
||||
import "./achievement-notification.scss";
|
||||
import { AchievementCustomNotificationPosition } from "@types";
|
||||
import {
|
||||
AchievementCustomNotificationPosition,
|
||||
AchievementNotificationInfo,
|
||||
} from "@types";
|
||||
|
||||
interface AchievementInfo {
|
||||
displayName: string;
|
||||
iconUrl: string;
|
||||
}
|
||||
|
||||
const NOTIFICATION_TIMEOUT = 4000;
|
||||
const NOTIFICATION_TIMEOUT = 6000;
|
||||
|
||||
export function AchievementNotification() {
|
||||
const { t } = useTranslation("achievement");
|
||||
@@ -20,9 +18,11 @@ export function AchievementNotification() {
|
||||
const [position, setPosition] =
|
||||
useState<AchievementCustomNotificationPosition>("top_left");
|
||||
|
||||
const [achievements, setAchievements] = useState<AchievementInfo[]>([]);
|
||||
const [achievements, setAchievements] = useState<
|
||||
AchievementNotificationInfo[]
|
||||
>([]);
|
||||
const [currentAchievement, setCurrentAchievement] =
|
||||
useState<AchievementInfo | null>(null);
|
||||
useState<AchievementNotificationInfo | null>(null);
|
||||
|
||||
const achievementAnimation = useRef(-1);
|
||||
const closingAnimation = useRef(-1);
|
||||
@@ -43,10 +43,14 @@ export function AchievementNotification() {
|
||||
|
||||
setAchievements([
|
||||
{
|
||||
displayName: t("new_achievements_unlocked", {
|
||||
title: t("new_achievements_unlocked", {
|
||||
gameCount,
|
||||
achievementCount,
|
||||
}),
|
||||
isHidden: false,
|
||||
isRare: false,
|
||||
isPlatinum: false,
|
||||
points: 0,
|
||||
iconUrl:
|
||||
"https://avatars.githubusercontent.com/u/164102380?s=400&u=01a13a7b4f0c642f7e547b8e1d70440ea06fa750&v=4",
|
||||
},
|
||||
@@ -63,10 +67,12 @@ export function AchievementNotification() {
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.onAchievementUnlocked(
|
||||
(_object, _shop, position, achievements) => {
|
||||
(position, achievements) => {
|
||||
if (!achievements?.length) return;
|
||||
if (position) {
|
||||
setPosition(position);
|
||||
}
|
||||
|
||||
setPosition(position);
|
||||
setAchievements((ach) => ach.concat(achievements));
|
||||
|
||||
playAudio();
|
||||
@@ -78,29 +84,6 @@ export function AchievementNotification() {
|
||||
};
|
||||
}, [playAudio]);
|
||||
|
||||
useEffect(() => {
|
||||
const unsubscribe = window.electron.onTestAchievementNotification(
|
||||
(position) => {
|
||||
setPosition(position);
|
||||
setAchievements((ach) =>
|
||||
ach.concat([
|
||||
{
|
||||
displayName: "Test Achievement",
|
||||
iconUrl:
|
||||
"https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Fc.tenor.com%2FRwKr7hVnXREAAAAC%2Fnyan-cat.gif&f=1&nofb=1&ipt=706fd8b00cbfb5b2d2621603834d5f32c0f34cce7113de228d2fcc2247a80318",
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
playAudio();
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, [playAudio]);
|
||||
|
||||
const hasAchievementsPending = achievements.length > 0;
|
||||
|
||||
const startAnimateClosing = useCallback(() => {
|
||||
@@ -169,15 +152,15 @@ export function AchievementNotification() {
|
||||
<div className="achievement-notification__content">
|
||||
<img
|
||||
src={currentAchievement.iconUrl}
|
||||
alt={currentAchievement.displayName}
|
||||
alt={currentAchievement.title}
|
||||
className="achievement-notification__icon"
|
||||
/>
|
||||
<div className="achievement-notification__text-container">
|
||||
<p className="achievement-notification__title">
|
||||
{t("achievement_unlocked")}
|
||||
{currentAchievement.title}
|
||||
</p>
|
||||
<p className="achievement-notification__description">
|
||||
{currentAchievement.displayName}
|
||||
{currentAchievement.description}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -122,8 +122,8 @@ export function SettingsGeneral() {
|
||||
"top_center",
|
||||
"top_right",
|
||||
"bottom_left",
|
||||
"bottom_right",
|
||||
"bottom_center",
|
||||
"bottom_right",
|
||||
].map((position) => ({
|
||||
key: position,
|
||||
value: position,
|
||||
|
||||
@@ -263,6 +263,15 @@ export type GameAchievementFiles = {
|
||||
[id: string]: AchievementFile[];
|
||||
};
|
||||
|
||||
export interface AchievementNotificationInfo {
|
||||
title: string;
|
||||
description?: string;
|
||||
iconUrl: string;
|
||||
isHidden: boolean;
|
||||
isRare: boolean;
|
||||
isPlatinum: boolean;
|
||||
points?: number;
|
||||
}
|
||||
export interface GameArtifact {
|
||||
id: string;
|
||||
artifactLengthInBytes: number;
|
||||
|
||||
Reference in New Issue
Block a user