Skip to content

Commit d14951f

Browse files
Merge pull request #1857 from hydralauncher/fix/use-local-achievement-cache
fix: achievements on library page
2 parents 8400edd + d6b3877 commit d14951f

File tree

8 files changed

+68
-40
lines changed

8 files changed

+68
-40
lines changed

python_rpc/main.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,11 @@ def profile_image():
153153
data = request.get_json()
154154
image_path = data.get('image_path')
155155

156+
# use webp as default value for target_extension
157+
target_extension = data.get('target_extension') or 'webp'
158+
156159
try:
157-
processed_image_path, mime_type = ProfileImageProcessor.process_image(image_path)
160+
processed_image_path, mime_type = ProfileImageProcessor.process_image(image_path, target_extension)
158161
return jsonify({'imagePath': processed_image_path, 'mimeType': mime_type}), 200
159162
except Exception as e:
160163
return jsonify({"error": str(e)}), 400

python_rpc/profile_image_processor.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
class ProfileImageProcessor:
55

66
@staticmethod
7-
def get_parsed_image_data(image_path):
7+
def get_parsed_image_data(image_path, target_extension):
88
Image.MAX_IMAGE_PIXELS = 933120000
99

1010
image = Image.open(image_path)
@@ -16,7 +16,7 @@ def get_parsed_image_data(image_path):
1616
return image_path, mime_type
1717
else:
1818
new_uuid = str(uuid.uuid4())
19-
new_image_path = os.path.join(tempfile.gettempdir(), new_uuid) + ".webp"
19+
new_image_path = os.path.join(tempfile.gettempdir(), new_uuid) + "." + target_extension
2020
image.save(new_image_path)
2121

2222
new_image = Image.open(new_image_path)
@@ -26,5 +26,5 @@ def get_parsed_image_data(image_path):
2626

2727

2828
@staticmethod
29-
def process_image(image_path):
30-
return ProfileImageProcessor.get_parsed_image_data(image_path)
29+
def process_image(image_path, target_extension):
30+
return ProfileImageProcessor.get_parsed_image_data(image_path, target_extension)

src/main/events/library/get-library.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { LibraryGame } from "@types";
22
import { registerEvent } from "../register-event";
33
import {
44
downloadsSublevel,
5+
gameAchievementsSublevel,
56
gamesShopAssetsSublevel,
67
gamesSublevel,
78
} from "@main/level";
@@ -18,11 +19,20 @@ const getLibrary = async (): Promise<LibraryGame[]> => {
1819
const download = await downloadsSublevel.get(key);
1920
const gameAssets = await gamesShopAssetsSublevel.get(key);
2021

22+
let unlockedAchievementCount = game.unlockedAchievementCount ?? 0;
23+
24+
if (!game.unlockedAchievementCount) {
25+
const achievements = await gameAchievementsSublevel.get(key);
26+
27+
unlockedAchievementCount =
28+
achievements?.unlockedAchievements.length ?? 0;
29+
}
30+
2131
return {
2232
id: key,
2333
...game,
2434
download: download ?? null,
25-
unlockedAchievementCount: game.unlockedAchievementCount ?? 0,
35+
unlockedAchievementCount,
2636
achievementCount: game.achievementCount ?? 0,
2737
// Spread gameAssets last to ensure all image URLs are properly set
2838
...gameAssets,
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
import { registerEvent } from "../register-event";
22
import { PythonRPC } from "@main/services/python-rpc";
33

4-
const processProfileImage = async (
4+
const processProfileImageEvent = async (
55
_event: Electron.IpcMainInvokeEvent,
66
path: string
77
) => {
8+
return processProfileImage(path, "webp");
9+
};
10+
11+
export const processProfileImage = async (path: string, extension?: string) => {
812
return PythonRPC.rpc
913
.post<{
1014
imagePath: string;
1115
mimeType: string;
12-
}>("/profile-image", { image_path: path })
16+
}>("/profile-image", { image_path: path, target_extension: extension })
1317
.then((response) => response.data);
1418
};
1519

16-
registerEvent("processProfileImage", processProfileImage);
20+
registerEvent("processProfileImage", processProfileImageEvent);

src/main/services/notifications/index.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ import { db, levelKeys, themesSublevel } from "@main/level";
1515
import { restartAndInstallUpdate } from "@main/events/autoupdater/restart-and-install-update";
1616
import { SystemPath } from "../system-path";
1717
import { getThemeSoundPath } from "@main/helpers";
18+
import { processProfileImage } from "@main/events/profile/process-profile-image";
19+
20+
const getStaticImage = async (path: string) => {
21+
return processProfileImage(path, "jpg")
22+
.then((response) => response.imagePath)
23+
.catch(() => path);
24+
};
1825

1926
async function downloadImage(url: string | null) {
2027
if (!url) return undefined;
@@ -31,8 +38,9 @@ async function downloadImage(url: string | null) {
3138
response.data.pipe(writer);
3239

3340
return new Promise<string | undefined>((resolve) => {
34-
writer.on("finish", () => {
35-
resolve(outputPath);
41+
writer.on("finish", async () => {
42+
const staticImagePath = await getStaticImage(outputPath);
43+
resolve(staticImagePath);
3644
});
3745
writer.on("error", () => {
3846
logger.error("Failed to download image", { url });

src/main/services/ws/events/friend-request.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ export const friendRequestEvent = async (payload: FriendRequest) => {
88
friendRequestCount: payload.friendRequestCount,
99
});
1010

11-
const user = await HydraApi.get(`/users/${payload.senderId}`);
11+
if (payload.senderId) {
12+
const user = await HydraApi.get(`/users/${payload.senderId}`);
1213

13-
if (user) {
14-
publishNewFriendRequestNotification(user);
14+
if (user) {
15+
publishNewFriendRequestNotification(user);
16+
}
1517
}
1618
};

src/renderer/src/pages/library/library-game-card-large.tsx

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { LibraryGame } from "@types";
22
import { useGameCard } from "@renderer/hooks";
33
import { ClockIcon, AlertFillIcon, TrophyIcon } from "@primer/octicons-react";
4-
import { memo, useMemo } from "react";
4+
import { memo, useEffect, useMemo, useState } from "react";
55
import "./library-game-card-large.scss";
66

77
interface LibraryGameCardLargeProps {
@@ -48,6 +48,20 @@ export const LibraryGameCardLarge = memo(function LibraryGameCardLarge({
4848
]
4949
);
5050

51+
const [unlockedAchievementsCount, setUnlockedAchievementsCount] = useState(
52+
game.unlockedAchievementCount ?? 0
53+
);
54+
55+
useEffect(() => {
56+
if (game.unlockedAchievementCount) return;
57+
58+
window.electron
59+
.getUnlockedAchievements(game.objectId, game.shop)
60+
.then((achievements) => {
61+
setUnlockedAchievementsCount(achievements.length);
62+
});
63+
}, [game]);
64+
5165
const backgroundStyle = useMemo(
5266
() =>
5367
backgroundImage ? { backgroundImage: `url(${backgroundImage})` } : {},
@@ -56,9 +70,9 @@ export const LibraryGameCardLarge = memo(function LibraryGameCardLarge({
5670

5771
const achievementBarStyle = useMemo(
5872
() => ({
59-
width: `${((game.unlockedAchievementCount ?? 0) / (game.achievementCount ?? 1)) * 100}%`,
73+
width: `${(unlockedAchievementsCount / (game.achievementCount ?? 1)) * 100}%`,
6074
}),
61-
[game.unlockedAchievementCount, game.achievementCount]
75+
[unlockedAchievementsCount, game.achievementCount]
6276
);
6377

6478
const logoImage = game.customLogoImageUrl ?? game.logoImageUrl;
@@ -116,14 +130,12 @@ export const LibraryGameCardLarge = memo(function LibraryGameCardLarge({
116130
className="library-game-card-large__achievement-trophy"
117131
/>
118132
<span className="library-game-card-large__achievement-count">
119-
{game.unlockedAchievementCount ?? 0} /{" "}
120-
{game.achievementCount ?? 0}
133+
{unlockedAchievementsCount} / {game.achievementCount ?? 0}
121134
</span>
122135
</div>
123136
<span className="library-game-card-large__achievement-percentage">
124137
{Math.round(
125-
((game.unlockedAchievementCount ?? 0) /
126-
(game.achievementCount ?? 1)) *
138+
(unlockedAchievementsCount / (game.achievementCount ?? 1)) *
127139
100
128140
)}
129141
%

src/renderer/src/pages/library/library.tsx

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@ import "./library.scss";
1414

1515
export default function Library() {
1616
const { library, updateLibrary } = useLibrary();
17-
type ElectronAPI = {
18-
refreshLibraryAssets?: () => Promise<unknown>;
19-
onLibraryBatchComplete?: (cb: () => void) => () => void;
20-
};
2117

2218
const [viewMode, setViewMode] = useState<ViewMode>(() => {
2319
const savedViewMode = localStorage.getItem("library-view-mode");
@@ -41,22 +37,15 @@ export default function Library() {
4137

4238
useEffect(() => {
4339
dispatch(setHeaderTitle(t("library")));
44-
const electron = (globalThis as unknown as { electron?: ElectronAPI })
45-
.electron;
46-
let unsubscribe: () => void = () => undefined;
47-
if (electron?.refreshLibraryAssets) {
48-
electron
49-
.refreshLibraryAssets()
50-
.then(() => updateLibrary())
51-
.catch(() => updateLibrary());
52-
if (electron.onLibraryBatchComplete) {
53-
unsubscribe = electron.onLibraryBatchComplete(() => {
54-
updateLibrary();
55-
});
56-
}
57-
} else {
40+
41+
const unsubscribe = window.electron.onLibraryBatchComplete(() => {
5842
updateLibrary();
59-
}
43+
});
44+
45+
window.electron
46+
.refreshLibraryAssets()
47+
.then(() => updateLibrary())
48+
.catch(() => updateLibrary());
6049

6150
return () => {
6251
unsubscribe();

0 commit comments

Comments
 (0)