Skip to content

Commit a9215e9

Browse files
authored
[Feat] Add reward claiming for other reward types (#927)
* add reward claiming for other reward types * prettier * update urls and types * fix typo * add quest playstreak GET, refactor claims to separate modules * fix screen quest playstreak * add resync tasks mutation * use mutate for other claims * fix claimPoints * fix quest fetch * clear playstreak cache after claim * update log * update quest details, add chain name and num to claim to rewards * fix lint * use prod dev portal url * rm console logs * update fetch with cookie * update hp ui * add LD quest claim cta flag * show current streak = required streak when current > required, refactor to shared fxn * add gamesToShowQuestsFor LD flag for quest overlay and main window page * fix wagmi urls, add polygon * [Feat] Add ERC721 and ERC1155 reward claiming (#936) * add erc1155 claiming * get sig for 1155 token id * add erc721 minting * fix 721 call args * handle null decimals and chain_id, fix show sync button * fix fetch with cookie * pretty * fix decimals check * improve fetch error logs * rm empty comment * bump version
1 parent 2c00cee commit a9215e9

File tree

22 files changed

+584
-102
lines changed

22 files changed

+584
-102
lines changed

package.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hyperplay",
3-
"version": "0.15.2",
3+
"version": "0.15.3",
44
"private": true,
55
"main": "build/main/main.js",
66
"homepage": "./",
@@ -167,9 +167,10 @@
167167
"@fortawesome/free-regular-svg-icons": "^6.1.1",
168168
"@fortawesome/free-solid-svg-icons": "^6.1.1",
169169
"@fortawesome/react-fontawesome": "^0.1.18",
170+
"@hyperplay/chains": "^0.2.10",
170171
"@hyperplay/check-disk-space": "^3.5.2",
171-
"@hyperplay/ui": "^1.5.9",
172-
"@hyperplay/utils": "^0.0.14",
172+
"@hyperplay/ui": "^1.5.28",
173+
"@hyperplay/utils": "^0.0.16",
173174
"@mantine/carousel": "^7.5.1",
174175
"@mantine/core": "^7.5.1",
175176
"@mantine/dropzone": "^7.5.1",

src/backend/api/questsAchievements.ts

+15-3
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,26 @@ export const getQuests = async (projectId?: string) =>
2424
export const getQuest = async (questId: number) =>
2525
ipcRenderer.invoke('getQuest', questId)
2626

27+
export const getUserPlayStreak = async (questId: number) =>
28+
ipcRenderer.invoke('getUserPlayStreak', questId)
29+
2730
export const getSteamGameMetadata = async (gameId: number) =>
2831
ipcRenderer.invoke('getSteamGameMetadata', gameId)
2932

3033
export const getQuestRewardSignature = async (
3134
address: `0x${string}`,
32-
questId: number,
33-
rewardId: number
34-
) => ipcRenderer.invoke('getQuestRewardSignature', address, questId, rewardId)
35+
rewardId: number,
36+
tokenId?: number
37+
) => ipcRenderer.invoke('getQuestRewardSignature', address, rewardId, tokenId)
3538

3639
export const getDepositContracts = async (questId: number) =>
3740
ipcRenderer.invoke('getDepositContracts', questId)
41+
42+
export const claimQuestPointsReward = async (rewardId: string) =>
43+
ipcRenderer.invoke('claimQuestPointsReward', rewardId)
44+
45+
export const completeExternalTask = async (rewardId: string) =>
46+
ipcRenderer.invoke('completeExternalTask', rewardId)
47+
48+
export const resyncExternalTask = async (rewardId: string) =>
49+
ipcRenderer.invoke('resyncExternalTask', rewardId)

src/backend/ipcHandlers/achievements.ts

+10-4
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,10 @@ ipcMain.handle('getSyncProgress', async (e, requestId) => {
244244

245245
async function getQuestRewardSignature(
246246
address: `0x${string}`,
247-
questId: number,
248-
rewardId: number
247+
rewardId: number,
248+
tokenId?: number
249249
): Promise<RewardClaimSignature> {
250-
const url = `${DEV_PORTAL_URL}api/v1/quests/${questId}/rewards/${rewardId}/signature`
250+
const url = `${DEV_PORTAL_URL}api/v1/quests/rewards/${rewardId}/signature`
251251

252252
const cookieString = await getPartitionCookies({
253253
partition: 'persist:auth',
@@ -261,9 +261,15 @@ async function getQuestRewardSignature(
261261
},
262262
body: JSON.stringify({
263263
withdraw: true,
264-
address
264+
address,
265+
tokenId
265266
})
266267
})
268+
if (!result.ok) {
269+
const errMsg = await result.text()
270+
logError(errMsg, LogPrefix.HyperPlay)
271+
throw errMsg
272+
}
267273
const resultJson = await result.json()
268274
return resultJson
269275
}

src/backend/ipcHandlers/quests.ts

+25
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { fetchWithCookie } from 'backend/utils/fetch_with_cookie'
12
import { DEV_PORTAL_URL } from 'common/constants'
3+
import { GenericApiResponse, PointsClaimReturn } from 'common/types'
24
import { ipcMain } from 'electron'
35

46
ipcMain.handle('getQuests', async (e, projectId) => {
@@ -17,8 +19,31 @@ ipcMain.handle('getQuest', async (e, questId) => {
1719
return questResultJson
1820
})
1921

22+
ipcMain.handle('getUserPlayStreak', async (e, questId) => {
23+
const questResultJson = await fetchWithCookie({
24+
url: `${DEV_PORTAL_URL}api/v1/quests/${questId}/playstreak`,
25+
method: 'GET'
26+
})
27+
return questResultJson
28+
})
29+
2030
ipcMain.handle('getSteamGameMetadata', async (e, gameId) => {
2131
const result = await fetch(`${DEV_PORTAL_URL}api/v1/steam/games/${gameId}`)
2232
const resultJson = await result.json()
2333
return resultJson
2434
})
35+
36+
ipcMain.handle('claimQuestPointsReward', async (e, rewardId) => {
37+
const url = `${DEV_PORTAL_URL}api/v1/quests/rewards/${rewardId}/points-claim`
38+
return (await fetchWithCookie({ url, method: 'POST' })) as PointsClaimReturn
39+
})
40+
41+
ipcMain.handle('completeExternalTask', async (e, rewardId) => {
42+
const url = `${DEV_PORTAL_URL}api/v1/quests/rewards/${rewardId}/external-tasks/completed`
43+
return (await fetchWithCookie({ url, method: 'POST' })) as GenericApiResponse
44+
})
45+
46+
ipcMain.handle('resyncExternalTask', async (e, rewardId) => {
47+
const url = `${DEV_PORTAL_URL}api/v1/quests/rewards/${rewardId}/external-tasks/re-sync`
48+
return (await fetchWithCookie({ url, method: 'POST' })) as GenericApiResponse
49+
})

src/backend/main.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -2004,9 +2004,11 @@ function watchLibraryChanges() {
20042004
sideloadLibraryStore.onDidChange('games', (newValue) =>
20052005
sendFrontendMessage('onLibraryChanged', 'sideload', newValue)
20062006
)
2007-
hpLibraryStore.onDidChange('games', (newValue) =>
2008-
sendFrontendMessage('onLibraryChanged', 'hyperplay', newValue)
2009-
)
2007+
hpLibraryStore.onDidChange('games', (newValue) => {
2008+
for (const win of BrowserWindow.getAllWindows()) {
2009+
win.webContents.send('onLibraryChanged', 'hyperplay', newValue)
2010+
}
2011+
})
20102012
}
20112013

20122014
ipcMain.on('openGameInEpicStore', async (_e, url) => {
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { DEV_PORTAL_URL } from 'common/constants'
2+
import getPartitionCookies from './get_partition_cookies'
3+
4+
export async function fetchWithCookie({
5+
url,
6+
method,
7+
partition
8+
}: {
9+
url: string
10+
method: string
11+
partition?: string
12+
}) {
13+
const cookieString = await getPartitionCookies({
14+
partition: partition ?? 'persist:auth',
15+
url: DEV_PORTAL_URL
16+
})
17+
18+
const response = await fetch(url, {
19+
method: method,
20+
headers: {
21+
Cookie: cookieString
22+
}
23+
})
24+
if (!response.ok) {
25+
throw await response.text()
26+
}
27+
const resultJson = await response.json()
28+
return resultJson
29+
}

src/common/typedefs/ipcBridge.d.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import {
44
Achievement,
55
SummaryAchievement,
66
LDEnv,
7-
HyperPlayRelease
7+
HyperPlayRelease,
8+
PointsClaimReturn,
9+
GenericApiResponse
810
} from './../types'
911
import { EventEmitter } from 'node:events'
1012
import { IpcMainEvent, OpenDialogOptions } from 'electron'
@@ -272,14 +274,18 @@ interface HyperPlayAsyncIPCFunctions {
272274
updateAutoLaunch: () => Promise<void>
273275
getQuests: (projectId?: string) => Promise<Quest[]>
274276
getQuest: (questId: number) => Promise<Quest>
277+
getUserPlayStreak: (questId: number) => Promise<UserPlayStreak>
275278
getSteamGameMetadata: (gameId: number) => Promise<unknown>
276279
getHyperPlayListings: () => Promise<Record<string, HyperPlayRelease>>
277280
getQuestRewardSignature: (
278281
address: `0x${string}`,
279-
questId: number,
280-
rewardId: number
282+
rewardId: number,
283+
tokenId?: number
281284
) => Promise<RewardClaimSignature>
282285
getDepositContracts: (questId: number) => Promise<DepositContract[]>
286+
claimQuestPointsReward: (rewardId: string) => Promise<PointsClaimReturn>
287+
completeExternalTask: (rewardId: string) => Promise<GenericApiResponse>
288+
resyncExternalTask: (rewardId: string) => Promise<GenericApiResponse>
283289
}
284290

285291
interface AsyncIPCFunctions extends HyperPlayAsyncIPCFunctions {

src/common/types.ts

+25-7
Original file line numberDiff line numberDiff line change
@@ -903,15 +903,15 @@ export type OverlayType = 'native' | 'browser' | 'mainWindow'
903903

904904
export interface Reward {
905905
id: number
906-
amount_per_user: number
907-
chain_id: number
906+
amount_per_user: number | null
907+
chain_id: number | null
908908
marketplace_url: string | null
909-
reward_type: 'ERC20' | 'ERC721' | 'ERC1155'
909+
reward_type: 'ERC20' | 'ERC721' | 'ERC1155' | 'POINTS' | 'EXTERNAL-TASKS'
910910
name: string
911911
contract_address: `0x${string}`
912-
decimals: number
912+
decimals: number | null
913913
/* eslint-disable-next-line */
914-
token_ids: any[]
914+
token_ids: { amount_per_user: string; token_id: number }[]
915915
image_url: string
916916
}
917917

@@ -930,8 +930,6 @@ export interface Quest {
930930
steam_games: { id: string }[]
931931
play_streak: {
932932
required_playstreak_in_days: number
933-
current_playstreak_in_days?: number
934-
last_play_session_completed_datetime_utc?: string
935933
}
936934
}
937935
}
@@ -946,3 +944,23 @@ export interface DepositContract {
946944
contract_address: `0x${string}`
947945
chain_id: number
948946
}
947+
948+
export interface GenericApiResponse {
949+
status?: string
950+
message: string
951+
}
952+
953+
export interface PointsClaimReturn {
954+
// sent on error
955+
status?: string
956+
message?: string
957+
// sent on success
958+
success?: string
959+
}
960+
961+
export interface UserPlayStreak {
962+
current_playstreak_in_days: number
963+
completed_counter: number
964+
accumulated_playtime_today_in_seconds: number
965+
last_play_session_completed_datetime: string
966+
}

src/frontend/OverlayManager/Overlay/index.tsx

+7-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { BrowserGameProps } from '../types'
1212
import { Button } from '@hyperplay/ui'
1313
import { QuestsViewer } from 'frontend/components/UI/QuestsViewer'
1414
import { useFlags } from 'launchdarkly-react-client-sdk'
15+
import libraryState from 'frontend/state/libraryState'
1516

1617
export const Overlay = observer(function ({
1718
appName,
@@ -110,7 +111,12 @@ export const Overlay = observer(function ({
110111
}
111112

112113
let questsViewer = null
113-
if (flags.questsOverlayClaimModals) {
114+
const gamesToShowQuestsFor =
115+
(flags.gamesToShowQuestsFor as string | undefined)?.split(',') ?? []
116+
if (
117+
flags.questsOverlayClaimModals ||
118+
libraryState.hasGame(gamesToShowQuestsFor)
119+
) {
114120
questsViewer = <QuestsViewer projectId={appName} />
115121
}
116122

0 commit comments

Comments
 (0)