Skip to content

Commit 0fb9df3

Browse files
authored
feat(quest-details): add claim in quest page (#1132)
* wip: quest page * tech(quest-details): add play click * chore: add npm packages * feat(quest-details): add reward claim toast * chore(i18n): add missing quest translations * chore(i18n): align comments * chore(i18n): align comments * chore(i18n): add missing translation keys * chore(claim-reward): remove auto claim * chore(quest-detail): no longer needed hook * fix(i18n): bad translation * fix(i18n): bad translation * tech(quest-details): remove empty file * tech(quest-details): replace hook with mobx class * tech(quest-detail): remove claim toast timeout * tech(quest-detail): make claimed toast observer * tech(quest-details): add on show metamask * tech(quest-detail): use claimedRewardToastState from quest-ui * chore: use latest ui package * tech(quest-details): show popup only if MM * tech(quest-detail): add missing change * tech(quest-detail): use latest quest-ui package * refactor(quest-detail): remove claim toast from overlay * chore: add ts-expect error link * chore: use stable quest-ui version
1 parent 9c5375e commit 0fb9df3

File tree

16 files changed

+417
-378
lines changed

16 files changed

+417
-378
lines changed

package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -169,8 +169,8 @@
169169
"@fortawesome/react-fontawesome": "^0.2.2",
170170
"@hyperplay/chains": "^0.3.0",
171171
"@hyperplay/check-disk-space": "^3.5.2",
172-
"@hyperplay/quests-ui": "^0.0.38",
173-
"@hyperplay/ui": "^1.8.9",
172+
"@hyperplay/quests-ui": "^0.1.0",
173+
"@hyperplay/ui": "^1.8.18",
174174
"@hyperplay/utils": "^0.3.4",
175175
"@mantine/carousel": "^7.12.0",
176176
"@mantine/core": "^7.12.0",
@@ -310,6 +310,7 @@
310310
"@lavamoat/allow-scripts": "^3.2.0",
311311
"@lavamoat/preinstall-always-fail": "^2.1.0",
312312
"@playwright/test": "^1.46.0",
313+
"@tanstack/react-query-devtools": "^5.59.20",
313314
"@testing-library/dom": "^7.31.2",
314315
"@testing-library/jest-dom": "^6.4.8",
315316
"@testing-library/react": "^14.3.1",
@@ -366,8 +367,8 @@
366367
"@hyperplay/extension-importer": "^0.0.4",
367368
"@hyperplay/extension-provider": "^0.0.8",
368369
"@hyperplay/mock-backend": "^0.0.1",
369-
"@hyperplay/patcher": "^0.0.17",
370370
"@hyperplay/overlay": "^0.0.7",
371+
"@hyperplay/patcher": "^0.0.17",
371372
"@hyperplay/providers": "^0.0.6",
372373
"@hyperplay/proxy-server": "^0.0.11"
373374
},

public/locales/en/translation.json

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -737,21 +737,40 @@
737737
"quest": {
738738
"active": "Active",
739739
"associatedGames": "Associated games",
740-
"claimAll": "Claim all",
740+
"claim": "Claim",
741741
"claimed": "Claimed",
742+
"claimFailed": "Claim failed",
743+
"claimFailedMessage": "Please try once more. If it still doesn't work, create a Discord support ticket.",
742744
"claimWarning": {
743745
"body": "<bold>IMPORTANT:</bold> Please ensure that you are allocating enough gas on the {{networkName}} network for the transaction to be successfully confirmed <bold>within 7 days.</bold>",
744746
"body2": "Otherwise, the Quest Reward <bold>will expire and will no longer be claimable.</bold>",
745747
"cancel": "Cancel",
746748
"confirm": "Confirm"
747749
},
748750
"connectSteamAccount": "Connect Steam account",
751+
"createDiscordTicket": "Create Discord Ticket",
749752
"g7Numclaimed": "Total Claimed G7 Credits",
750753
"linkAccount": "Link your Steam account to check eligibility.",
751754
"needMoreAchievements": "You need to have completed {{percent}}% of the achievements in one of these games.",
752755
"noG7ConnectionClaim": "You need to have a Game7 account linked to {{email}} to claim your rewards.",
753-
"noG7ConnectionSync": "You need to have a Game7 account linked to {{email}} to resync your tasks.",
754-
"notEnoughGas": "Insufficient wallet balance to claim your reward due to gas fees. Try a different wallet or replenish this one before retrying.",
756+
"noG7ConnectionSync": {
757+
"message": "You need to have a Game7 account linked to {{email}} to resync your tasks.",
758+
"title": "No G7 account linked"
759+
},
760+
"notEligible": {
761+
"message": "You have not completed the required play streak days and can not claim your reward at this time.",
762+
"title": "Not eligible yet"
763+
},
764+
"notEnoughBalance": {
765+
"title": "Low balance"
766+
},
767+
"notEnoughGas": {
768+
"message": "Insufficient wallet balance to claim your reward due to gas fees. Try a different wallet or replenish this one before retrying."
769+
},
770+
"notSignedIn": {
771+
"message": "You need to be signed in to claim your reward.",
772+
"title": "Not signed in"
773+
},
755774
"playstreak": {
756775
"dayResets": "Day resets:",
757776
"days": "days",
@@ -766,16 +785,22 @@
766785
},
767786
"points": "Points",
768787
"pointsClaimed": "Points Claimed",
788+
"questAvailable": "Quest available!",
789+
"questComplete": "Quest complete!",
769790
"quests": "Quests",
770791
"readyForClaim": "Ready for claim",
771792
"reputation": "Reputation",
772793
"reward": "Reward",
794+
"rewardClaimed": "Claim successful",
773795
"signIn": "Log in",
774796
"sync": "Sync",
797+
"toClaimReward": "to claim your reward.",
798+
"toSeeDetails": "to see details.",
775799
"type": {
776-
"playstreak": "Play Streak"
800+
"playstreak": "Play Streak",
801+
"reputation": "Reputation"
777802
},
778-
"View Game": "View Game"
803+
"youHaveClaimed": "You have claimed {{totalRewards}} reward{{plural}}."
779804
},
780805
"quests": {
781806
"g7CreditsModal": {

src/frontend/OverlayManager/Overlay/index.module.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
flex-direction: column;
5252
gap: var(--space-2lg);
5353
padding: var(--space-2lg-fixed) var(--space-xl-fixed);
54+
align-items: flex-end;
5455
}
5556

5657
.hideOverlay {

src/frontend/OverlayManager/Overlay/index.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const Overlay = observer(function ({
2424
runner
2525
}: BrowserGameProps) {
2626
const flags = useFlags()
27+
2728
const txnToastContainerStyle = {} as React.CSSProperties
2829
if (OverlayState.title === 'HyperPlay Toasts') {
2930
txnToastContainerStyle.bottom = 'unset'
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import React, { useCallback } from 'react'
2+
import {
3+
QuestDetailsWrapper,
4+
claimedRewardToastState,
5+
useGetUserPlayStreak
6+
} from '@hyperplay/quests-ui'
7+
import { Reward, Quest } from '@hyperplay/utils'
8+
import useAuthSession from 'frontend/hooks/useAuthSession'
9+
import authState from 'frontend/state/authState'
10+
import { useFlags } from 'launchdarkly-react-client-sdk'
11+
import { useTranslation } from 'react-i18next'
12+
import { useAccount } from 'wagmi'
13+
import { useSyncPlayStreakWithExternalSource } from 'frontend/hooks/useSyncPlayStreakWithExternalSource'
14+
import extensionState from 'frontend/state/ExtensionState'
15+
16+
/**
17+
* Don't delete this comment block since it's used for translation parsing for keys that are on the quests-ui library.
18+
* As a heads up, everytime you add a new key on any library, you need to add it as a block comment anywhere in the code as well.
19+
*
20+
* t("quest.noG7ConnectionClaim", "You need to have a Game7 account linked to {{email}} to claim your rewards.")
21+
* t("quest.noG7ConnectionSync", "You need to have a Game7 account linked to {{email}} to resync your tasks.")
22+
* t("quest.notEnoughGas", "Insufficient wallet balance to claim your reward due to gas fees. Try a different wallet or replenish this one before retrying.")
23+
* t("quest.playstreak.syncSuccess", "Progress synced")
24+
* t("quest.claim", "Claim")
25+
* t('quest.notSignedIn.title', 'Not signed in')
26+
* t('quest.notSignedIn.message', 'You need to be signed in to claim your reward.')
27+
* t('quest.notEligible.title', 'Not eligible yet')
28+
* t('quest.notEligible.message', 'You have not completed the required play streak days and can not claim your reward at this time.')
29+
* t('quest.playstreak.sync', 'Sync Progress')
30+
* t('quest.noG7ConnectionSync.title', 'No G7 account linked')
31+
* t('quest.noG7ConnectionSync.message', 'You need to have a Game7 account linked to {{email}} to claim your rewards.')
32+
* t('quest.notEnoughBalance.title', 'Low balance')
33+
* t('quest.notEnoughGas.message', 'Insufficient wallet balance to claim your reward due to gas fees. Try a different wallet or replenish this one before retrying.')
34+
* t('quest.claimFailed', 'Claim failed')
35+
* t('quest.claimFailedMessage', 'Please try once more. If it still doesn\'t work, create a Discord support ticket.')
36+
* t('quest.createDiscordTicket', 'Create Discord Ticket')
37+
* t('quest.reward', 'Rewards')
38+
* t('quest.associatedGames', 'Associated games')
39+
* t('quest.linkAccount', 'Link your Steam account to check eligibility.')
40+
* t('quest.needMoreAchievements', 'You need to have completed {{percent}}% of the achievements in one of these games.')
41+
* t('quest.claim', 'Claim')
42+
* t('quest.signIn', 'Sign in')
43+
* t('quest.connectSteamAccount', 'Connect Steam account')
44+
* t('quest.type.reputation', 'Reputation')
45+
* t('quest.type.playstreak', 'Play Streak')
46+
* t('quest.sync', 'Sync')
47+
* t('quest.playstreak.streakProgress', 'Streak Progress')
48+
* t('quest.playstreak.days', 'days')
49+
* t('quest.playstreak.playToStart', 'Play this game to start your streak!')
50+
* t('quest.playstreak.playEachDay', 'Play each day so your streak won\'t reset!')
51+
* t('quest.playstreak.streakCompleted', 'Streak completed! Claim your rewards now.')
52+
* t('quest.playstreak.now', 'Now')
53+
* t('quest.playstreak.dayResets', 'Day resets:')
54+
* t('quest.playstreak.progressTowardsStreak', 'progress towards today\'s streak.')
55+
* t('quest.points', 'Points')
56+
* t('quest.claimWarning.body', '<bold>IMPORTANT:</bold> Please ensure that you are allocating enough gas on the {{networkName}} network for the transaction to be successfully confirmed <bold>within 7 days.</bold>')
57+
* t('quest.claimWarning.body2', 'Otherwise, the Quest Reward <bold>will expire and will no longer be claimable.</bold>')
58+
* t('quest.claimWarning.cancel', 'Cancel')
59+
* t('quest.claimWarning.confirm', 'Confirm')
60+
*/
61+
62+
export default function QuestDetails({
63+
questId,
64+
className,
65+
isQuestsPage,
66+
onPlayClick
67+
}: {
68+
questId: number | null
69+
className?: string
70+
isQuestsPage?: boolean
71+
onPlayClick?: (quest: Quest) => void
72+
}) {
73+
const { address } = useAccount()
74+
const { isSignedIn, data } = useAuthSession()
75+
const { t } = useTranslation()
76+
const flags = useFlags()
77+
const sessionEmail = data?.linkedAccounts.get('email')
78+
const { invalidateQuery } = useGetUserPlayStreak(
79+
questId,
80+
window.api.getUserPlayStreak
81+
)
82+
83+
const getPendingExternalSync = useCallback(async () => {
84+
if (!address || !questId || !isSignedIn) return false
85+
return window.api.checkPendingSync({
86+
questId,
87+
wallet: address
88+
})
89+
}, [address, questId, isSignedIn])
90+
91+
const { syncPlayStreakWithExternalSource } =
92+
useSyncPlayStreakWithExternalSource({
93+
refreshPlayStreak: invalidateQuery
94+
})
95+
96+
return (
97+
<QuestDetailsWrapper
98+
// @ts-expect-error - see: https://github.com/qmhc/vite-plugin-dts/issues/330
99+
onRewardClaimed={(reward) =>
100+
claimedRewardToastState.showClaimedReward(reward)
101+
}
102+
onShowMetaMaskPopup={async () => {
103+
const currentProvider = await window.api.getConnectedProvider()
104+
if (currentProvider === 'MetaMaskExtension') {
105+
extensionState.showPopup()
106+
}
107+
}}
108+
onPlayClick={onPlayClick}
109+
getPendingExternalSync={getPendingExternalSync}
110+
syncPlayStreakWithExternalSource={syncPlayStreakWithExternalSource}
111+
tOverride={t}
112+
sessionEmail={sessionEmail}
113+
className={className}
114+
checkG7ConnectionStatus={window.api.checkG7ConnectionStatus}
115+
logInfo={window.api.logInfo}
116+
logError={window.api.logError}
117+
flags={{
118+
rewardTypeClaimEnabled: {
119+
ERC20: flags.erc20RewardsClaim,
120+
ERC721: flags.erc721RewardsClaim,
121+
ERC1155: flags.erc1155RewardsClaim,
122+
POINTS: flags.pointsRewardsClaim,
123+
'EXTERNAL-TASKS': flags.externalTasksRewardsClaim
124+
},
125+
questsOverlayClaimCtaEnabled: flags.questsOverlayClaimCtaEnabled
126+
}}
127+
trackEvent={window.api.trackEvent}
128+
signInWithSteamAccount={() => window.api.signInWithProvider('steam')}
129+
openDiscordLink={window.api.openDiscordLink}
130+
selectedQuestId={questId}
131+
getQuest={window.api.getQuest}
132+
getUserPlayStreak={window.api.getUserPlayStreak}
133+
getSteamGameMetadata={window.api.getSteamGameMetadata}
134+
claimPoints={async (reward: Reward) =>
135+
window.api.claimQuestPointsReward(reward.id.toString())
136+
}
137+
completeExternalTask={async (reward: Reward) =>
138+
window.api.completeExternalTask(reward.id.toString())
139+
}
140+
getQuestRewardSignature={window.api.getQuestRewardSignature}
141+
confirmRewardClaim={window.api.confirmRewardClaim}
142+
getExternalTaskCredits={window.api.getExternalTaskCredits}
143+
syncPlaySession={window.api.syncPlaySession}
144+
getDepositContracts={window.api.getDepositContracts}
145+
openSignInModal={authState.openSignInModal}
146+
resyncExternalTask={async (rewardId: string) => {
147+
window.api.resyncExternalTask(rewardId)
148+
}}
149+
isSignedIn={isSignedIn}
150+
isQuestsPage={isQuestsPage}
151+
key={'questDetailsLoading'}
152+
/>
153+
)
154+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react'
2+
import { ToastQuest } from '@hyperplay/ui'
3+
import Draggable from 'react-draggable'
4+
import { useTranslation } from 'react-i18next'
5+
import { observer } from 'mobx-react-lite'
6+
import { claimedRewardToastState } from '@hyperplay/quests-ui'
7+
8+
type QuestRewardClaimedToastProps = {
9+
draggable?: boolean
10+
className?: string
11+
}
12+
13+
export const QuestRewardClaimedToast = observer(
14+
({ draggable = true, className }: QuestRewardClaimedToastProps) => {
15+
const { t } = useTranslation()
16+
17+
if (!claimedRewardToastState.claimedReward) {
18+
return null
19+
}
20+
21+
const toast = (
22+
<div className={className}>
23+
<ToastQuest
24+
i18n={{
25+
overlayToggleKey: 'X',
26+
overlayToggleModKey: 'option',
27+
plus: '+',
28+
questAvailable: t('quest.questAvailable', 'Quest available!'),
29+
questComplete: t('quest.questComplete', 'Quest complete!'),
30+
rewardClaimed: t('quest.rewardClaimed', 'Claim successful'),
31+
toClaimReward: t('quest.toClaimReward', 'to claim your reward.'),
32+
toSeeDetails: t('quest.toSeeDetails', 'to see details.'),
33+
youHaveClaimed: t(
34+
'quest.youHaveClaimed',
35+
'You have claimed {{totalRewards}} reward{{plural}}.',
36+
{
37+
totalRewards: 1,
38+
plural: ''
39+
}
40+
)
41+
}}
42+
onCloseClick={() => claimedRewardToastState.clearReward()}
43+
status="claimed"
44+
/>
45+
</div>
46+
)
47+
48+
return draggable ? <Draggable>{toast}</Draggable> : toast
49+
}
50+
)

src/frontend/components/UI/QuestsViewer/components/QuestLogWrapper/index.module.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@
22
margin: 0 var(--space-sm) 0 0;
33
min-width: 320px;
44
max-width: 400px;
5+
width: 100%;
56
}

0 commit comments

Comments
 (0)