diff --git a/src/main/events/user/get-unlocked-achievements.ts b/src/main/events/user/get-unlocked-achievements.ts index 581b75dd..cfdfaa4f 100644 --- a/src/main/events/user/get-unlocked-achievements.ts +++ b/src/main/events/user/get-unlocked-achievements.ts @@ -85,8 +85,7 @@ export const getUnlockedAchievements = async ( ...achievementData, unlocked: true, unlockTime: unlockedAchievementData.unlockTime, - achievementImageUrl: - remoteAchievementData?.achievementImageUrl || null, + imageUrl: remoteAchievementData?.imageUrl || null, }; } @@ -99,7 +98,7 @@ export const getUnlockedAchievements = async ( !achievementData.hidden || showHiddenAchievementsDescription ? achievementData.description : undefined, - achievementImageUrl: remoteAchievementData?.achievementImageUrl || null, + imageUrl: remoteAchievementData?.imageUrl || null, }; }) .sort((a, b) => { diff --git a/src/main/services/achievements/achievement-image-service.ts b/src/main/services/achievements/achievement-image-service.ts index c335f4e8..92fb7c4b 100644 --- a/src/main/services/achievements/achievement-image-service.ts +++ b/src/main/services/achievements/achievement-image-service.ts @@ -15,7 +15,7 @@ export class AchievementImageService { const response = await HydraApi.post<{ presignedUrl: string; - achievementImageUrl: string; + imageUrl: string; }>("/presigned-urls/achievement-image", { imageExt: path.extname(imagePath).slice(1), imageLength: fileSizeInBytes, @@ -29,7 +29,7 @@ export class AchievementImageService { }, }); - return response.achievementImageUrl; + return response.imageUrl; } private static async storeImageLocally(imagePath: string): Promise { @@ -40,17 +40,6 @@ export class AchievementImageService { return `data:${mimeType?.mime || "image/jpeg"};base64,${base64Image}`; } - private static async updateAchievementWithImageUrl( - shop: GameShop, - gameId: string, - achievementName: string, - imageUrl: string - ): Promise { - await HydraApi.patch( - `/profile/games/achievements/${shop}/${gameId}/${achievementName}/image`, - { achievementImageUrl: imageUrl } - ); - } private static async hasActiveSubscription(): Promise { return db @@ -75,7 +64,7 @@ export class AchievementImageService { if (existingData) { await gameAchievementsSublevel.put(achievementKey, { ...existingData, - achievementImageUrl: imageUrl, + imageUrl, }); } } @@ -99,8 +88,7 @@ export class AchievementImageService { static async uploadAchievementImage( gameId: string, achievementName: string, - imagePath: string, - shop?: GameShop + imagePath: string ): Promise<{ success: boolean; imageUrl: string }> { try { let imageUrl: string; @@ -109,14 +97,9 @@ export class AchievementImageService { if (hasSubscription) { imageUrl = await this.uploadImageToCDN(imagePath); - if (shop) { - await this.updateAchievementWithImageUrl( - shop, - gameId, - achievementName, - imageUrl - ); - } + // Removed per new single-call sync: image URL will be included + // in the PUT /profile/games/achievements payload later. + // No direct API call here anymore. logger.log( `Achievement image uploaded to CDN for ${gameId}:${achievementName}` ); @@ -155,8 +138,7 @@ export class AchievementImageService { const result = await this.uploadAchievementImage( gameId, achievementName, - imagePath, - shop + imagePath ); await this.updateLocalAchievementData(shop, gameId, result.imageUrl); diff --git a/src/main/services/achievements/merge-achievements.ts b/src/main/services/achievements/merge-achievements.ts index 8d07ecf6..b0133420 100644 --- a/src/main/services/achievements/merge-achievements.ts +++ b/src/main/services/achievements/merge-achievements.ts @@ -161,6 +161,65 @@ export const mergeAchievements = async ( } } + // For subscribers, capture and upload screenshots first to get image URLs + let achievementsWithImages = [...mergedLocalAchievements]; + + if ( + newAchievements.length && + userPreferences.enableAchievementScreenshots === true + ) { + try { + for (const achievement of newAchievements) { + try { + const achievementData = achievementsData.find( + (steamAchievement) => { + return ( + achievement.name.toUpperCase() === + steamAchievement.name.toUpperCase() + ); + } + ); + + const achievementDisplayName = + achievementData?.displayName || achievement.name; + + const screenshotPath = + await ScreenshotService.captureDesktopScreenshot( + game.title, + achievementDisplayName + ); + + const uploadResult = await AchievementImageService.uploadAchievementImage( + game.objectId, + achievement.name, + screenshotPath + ); + + // Update the achievement with the image URL for API sync + const achievementIndex = achievementsWithImages.findIndex( + (a) => a.name.toUpperCase() === achievement.name.toUpperCase() + ); + if (achievementIndex !== -1 && uploadResult.imageUrl) { + achievementsWithImages[achievementIndex] = { + ...achievementsWithImages[achievementIndex], + imageUrl: uploadResult.imageUrl, + }; + } + } catch (error) { + achievementsLogger.error( + "Failed to upload achievement image", + error + ); + } + } + } catch (error) { + achievementsLogger.error( + "Failed to capture screenshot for achievement", + error + ); + } + } + const shouldSyncWithRemote = game.remoteId && (newAchievements.length || AchievementWatcherManager.hasFinishedPreSearch); @@ -170,7 +229,7 @@ export const mergeAchievements = async ( "/profile/games/achievements", { id: game.remoteId, - achievements: mergedLocalAchievements, + achievements: achievementsWithImages, }, { needsSubscription: !newAchievements.length } ) @@ -186,56 +245,10 @@ export const mergeAchievements = async ( await saveAchievementsOnLocal( game.objectId, game.shop, - mergedLocalAchievements, + achievementsWithImages, publishNotification ); } - - if ( - newAchievements.length && - userPreferences.enableAchievementScreenshots === true - ) { - try { - for (const achievement of newAchievements) { - try { - const achievementData = achievementsData.find( - (steamAchievement) => { - return ( - achievement.name.toUpperCase() === - steamAchievement.name.toUpperCase() - ); - } - ); - - const achievementDisplayName = - achievementData?.displayName || achievement.name; - - const screenshotPath = - await ScreenshotService.captureDesktopScreenshot( - game.title, - achievementDisplayName - ); - - await AchievementImageService.uploadAchievementImage( - game.objectId, - achievement.name, - screenshotPath, - game.shop - ); - } catch (error) { - achievementsLogger.error( - "Failed to upload achievement image", - error - ); - } - } - } catch (error) { - achievementsLogger.error( - "Failed to capture screenshot for achievement", - error - ); - } - } }) .catch((err) => { if (err instanceof SubscriptionRequiredError) { @@ -249,7 +262,7 @@ export const mergeAchievements = async ( return saveAchievementsOnLocal( game.objectId, game.shop, - mergedLocalAchievements, + achievementsWithImages, publishNotification ); }) @@ -260,7 +273,7 @@ export const mergeAchievements = async ( await saveAchievementsOnLocal( game.objectId, game.shop, - mergedLocalAchievements, + achievementsWithImages, publishNotification ); } diff --git a/src/renderer/src/pages/achievements/achievement-list.tsx b/src/renderer/src/pages/achievements/achievement-list.tsx index 22b768d4..2fa92838 100644 --- a/src/renderer/src/pages/achievements/achievement-list.tsx +++ b/src/renderer/src/pages/achievements/achievement-list.tsx @@ -63,16 +63,16 @@ export function AchievementList({
- {achievement.achievementImageUrl && achievement.unlocked && ( + {achievement.imageUrl && achievement.unlocked && (
-
+
-
-
+
+
- + {achievement.name}
-
-
+
+
- + {achievement.gameTitle}
-
+
))}
diff --git a/src/types/game.types.ts b/src/types/game.types.ts index 0494f2d3..f11ab923 100644 --- a/src/types/game.types.ts +++ b/src/types/game.types.ts @@ -5,6 +5,7 @@ export type ShortcutLocation = "desktop" | "start_menu"; export interface UnlockedAchievement { name: string; unlockTime: number; + imageUrl?: string | null; } export interface SteamAchievement { @@ -20,5 +21,5 @@ export interface SteamAchievement { export interface UserAchievement extends SteamAchievement { unlocked: boolean; unlockTime: number | null; - achievementImageUrl?: string | null; + imageUrl?: string | null; } diff --git a/src/types/index.ts b/src/types/index.ts index 61889c5b..f1bd496c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -196,7 +196,7 @@ export interface UserDetails { export interface ProfileAchievement { name: string; - achievementImageUrl: string; + imageUrl: string; unlockTime: number; gameTitle: string; gameIconUrl: string; diff --git a/src/types/level.types.ts b/src/types/level.types.ts index da3915f5..289f8dc0 100644 --- a/src/types/level.types.ts +++ b/src/types/level.types.ts @@ -83,7 +83,7 @@ export interface GameAchievement { achievements: SteamAchievement[]; unlockedAchievements: UnlockedAchievement[]; updatedAt: number | undefined; - achievementImageUrl?: string | null; + imageUrl?: string | null; language?: string; }