Skip to content

Commit fbf8c78

Browse files
committed
feat: add top navigation tour functionality and steps
1 parent 5309dc8 commit fbf8c78

File tree

8 files changed

+155
-20
lines changed

8 files changed

+155
-20
lines changed

src/frontend/components/TourGuide/TourContext.tsx

+8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ type TourState = {
77
activateLibraryTour: () => void
88
activateSidebarTour: () => void
99
activateGamePageTour: () => void
10+
activateTopNavTour: () => void
1011
deactivateTour: () => void
1112
markTourAsComplete: (tourId: string) => void
1213
isTourCompleted: (tourId: string) => boolean
@@ -19,6 +20,7 @@ const initialState: TourState = {
1920
activateLibraryTour: () => {},
2021
activateSidebarTour: () => {},
2122
activateGamePageTour: () => {},
23+
activateTopNavTour: () => {},
2224
deactivateTour: () => {},
2325
markTourAsComplete: () => {},
2426
isTourCompleted: () => false
@@ -54,6 +56,11 @@ export const TourProvider: React.FC<TourProviderProps> = ({ children }) => {
5456
setIsTourActive(true)
5557
}
5658

59+
const activateTopNavTour = () => {
60+
setCurrentTour('topnav')
61+
setIsTourActive(true)
62+
}
63+
5764
const deactivateTour = () => {
5865
setIsTourActive(false)
5966
setCurrentTour(null)
@@ -80,6 +87,7 @@ export const TourProvider: React.FC<TourProviderProps> = ({ children }) => {
8087
activateLibraryTour,
8188
activateSidebarTour,
8289
activateGamePageTour,
90+
activateTopNavTour,
8391
deactivateTour,
8492
markTourAsComplete,
8593
isTourCompleted

src/frontend/components/TourGuide/TourGuide.scss

+7-3
Original file line numberDiff line numberDiff line change
@@ -72,12 +72,16 @@
7272
}
7373

7474
.introjs-helperLayer {
75-
background-color: rgba(255, 255, 255, 0.1);
75+
background-color: transparent;
7676
border: 2px solid var(--primary);
77-
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.4);
77+
box-shadow: 0 2px 15px rgba(0, 0, 0, 0.2);
7878
}
7979

8080
.introjs-overlay {
81-
opacity: 0.7 !important;
81+
opacity: 0.5 !important;
8282
background-color: var(--background-darker);
8383
}
84+
85+
.introjs-fixParent {
86+
z-index: 999999 !important;
87+
}

src/frontend/components/TourGuide/TourGuide.tsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
libraryTourSteps,
88
sidebarTourSteps,
99
gamePageTourSteps,
10+
topNavTourSteps,
1011
TourStep
1112
} from './TourSteps'
1213
import './TourGuide.scss'
@@ -33,7 +34,9 @@ export const TourGuide: React.FC = () => {
3334
case 'gamepage':
3435
steps = gamePageTourSteps(t)
3536
break
36-
// Add more tour types here as needed
37+
case 'topnav':
38+
steps = topNavTourSteps(t)
39+
break
3740
default:
3841
steps = []
3942
}

src/frontend/components/TourGuide/TourSteps.ts

+68
Original file line numberDiff line numberDiff line change
@@ -253,3 +253,71 @@ export const gamePageTourSteps = (t: TFunction): TourStep[] => [
253253
position: 'center'
254254
}
255255
]
256+
257+
// TopNav tour steps
258+
export const topNavTourSteps = (t: TFunction): TourStep[] => [
259+
{
260+
element: '[data-tour="topnav-container"]',
261+
intro: t(
262+
'tour.topnav.welcome',
263+
"Welcome to HyperPlay! Let's explore the navigation bar and its features."
264+
),
265+
position: 'bottom'
266+
},
267+
{
268+
element: '[data-tour="topnav-hyperplay-store"]',
269+
intro: t(
270+
'tour.topnav.hyperplay_store',
271+
'Access the HyperPlay Store to discover exciting web3 games.'
272+
),
273+
position: 'bottom'
274+
},
275+
{
276+
element: '[data-tour="topnav-epic-store"]',
277+
intro: t(
278+
'tour.topnav.epic_store',
279+
'Browse and buy games from the Epic Games Store.'
280+
),
281+
position: 'bottom'
282+
},
283+
{
284+
element: '[data-tour="topnav-gog-store"]',
285+
intro: t(
286+
'tour.topnav.gog_store',
287+
'Browse and buy games from the GOG Store.'
288+
),
289+
position: 'bottom'
290+
},
291+
{
292+
element: '[data-testid="searchBar"]',
293+
intro: t(
294+
'tour.library.search',
295+
'Search for specific games by title using the search bar.'
296+
),
297+
position: 'bottom'
298+
},
299+
{
300+
element: '[data-tour="topnav-metamask"]',
301+
intro: t(
302+
'tour.topnav.metamask',
303+
'Quick access to your MetaMask wallet for web3 transactions.'
304+
),
305+
position: 'left'
306+
},
307+
{
308+
element: '[data-tour="topnav-account"]',
309+
intro: t(
310+
'tour.topnav.account',
311+
'Change your connected wallet, manage connected stores, view your portfolio, and more.'
312+
),
313+
position: 'left'
314+
},
315+
{
316+
element: '[data-tour="topnav-container"]',
317+
intro: t(
318+
'tour.topnav.finish',
319+
"That's it! You now know how to navigate HyperPlay's top bar and account features."
320+
),
321+
position: 'center'
322+
}
323+
]

src/frontend/components/TourGuide/TourTriggerButton.tsx

+10-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
55
import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'
66
import { useTourGuide } from './TourContext'
77

8-
export type TourType = 'library' | 'sidebar' | 'gamepage'
8+
export type TourType = 'library' | 'sidebar' | 'gamepage' | 'topnav'
99

1010
interface TourTriggerButtonProps {
1111
tourId?: TourType
@@ -23,8 +23,12 @@ export const TourTriggerButton: React.FC<TourTriggerButtonProps> = ({
2323
showText = true
2424
}) => {
2525
const { t } = useTranslation('tour')
26-
const { activateLibraryTour, activateSidebarTour, activateGamePageTour } =
27-
useTourGuide()
26+
const {
27+
activateLibraryTour,
28+
activateSidebarTour,
29+
activateGamePageTour,
30+
activateTopNavTour
31+
} = useTourGuide()
2832

2933
const handleClick = () => {
3034
switch (tourId) {
@@ -37,6 +41,9 @@ export const TourTriggerButton: React.FC<TourTriggerButtonProps> = ({
3741
case 'gamepage':
3842
activateGamePageTour()
3943
break
44+
case 'topnav':
45+
activateTopNavTour()
46+
break
4047
default:
4148
activateLibraryTour()
4249
}

src/frontend/components/UI/AccountDropdown/index.tsx

+21-7
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,18 @@ import useAuthSession from '../../../hooks/useAuthSession'
1717
function NavigationMenuItem({
1818
label,
1919
to,
20-
showMetaMaskExtensionLinks
20+
showMetaMaskExtensionLinks,
21+
dataTour
2122
}: {
2223
label: string
2324
to: string
2425
showMetaMaskExtensionLinks: boolean
26+
dataTour?: string
2527
}) {
2628
return (
2729
<Menu.Item
2830
id={showMetaMaskExtensionLinks ? 'topMenuItemWalletDropdown' : undefined}
31+
data-tour={dataTour}
2932
>
3033
<NavLink
3134
to={to}
@@ -74,6 +77,7 @@ const WalletDropdown: React.FC = observer(() => {
7477
<Menu.Item
7578
className={styles.menuItem}
7679
onClick={() => onboardingStore.openOnboarding()}
80+
data-tour="account-wallet-connect"
7781
>
7882
<div className={`body ${styles.itemContents}`}>
7983
{showWalletConnectedLinks
@@ -87,25 +91,28 @@ const WalletDropdown: React.FC = observer(() => {
8791
label={t('hyperplay.viewFullscreen', `View fullscreen`)}
8892
to={'/metamaskHome'}
8993
showMetaMaskExtensionLinks={showMetaMaskExtensionLinks}
90-
></NavigationMenuItem>
94+
dataTour="account-metamask-fullscreen"
95+
/>
9196
<NavigationMenuItem
9297
label={t('hyperplay.viewItem', {
9398
defaultValue: 'View {{item}}',
9499
item: 'Snaps'
95100
})}
96101
showMetaMaskExtensionLinks={showMetaMaskExtensionLinks}
97102
to={'/metamaskSnaps'}
98-
></NavigationMenuItem>
103+
dataTour="account-metamask-snaps"
104+
/>
99105
</>
100106
)}
101107
{showMetaMaskExtensionLinks && (
102108
<Menu.Item
103-
className={`${styles.menuItem} `}
109+
className={styles.menuItem}
104110
id={
105111
!showWalletConnectedLinks
106112
? 'topMenuItemWalletDropdown'
107113
: undefined
108114
}
115+
data-tour="account-metamask-portfolio"
109116
>
110117
<NavLink
111118
to={'/metamaskPortfolio'}
@@ -127,7 +134,7 @@ const WalletDropdown: React.FC = observer(() => {
127134
</Menu.Item>
128135
)}
129136
<Menu.Label>Epic/GoG {t('accounts', `accounts`)}</Menu.Label>
130-
<Menu.Item>
137+
<Menu.Item data-tour="account-manage-stores">
131138
<NavLink to={'/login'}>
132139
<div className={`body ${styles.itemContents}`}>
133140
{t('userselector.manageStore', `Manage stores`)}
@@ -140,7 +147,10 @@ const WalletDropdown: React.FC = observer(() => {
140147
<Menu.Label>HyperPlay {t('profile', `Profile`)}</Menu.Label>
141148
{isSignedIn ? (
142149
<>
143-
<Menu.Item onClick={() => authState.openSignInModal()}>
150+
<Menu.Item
151+
onClick={() => authState.openSignInModal()}
152+
data-tour="account-manage-accounts"
153+
>
144154
<div className={`body ${styles.itemContents}`}>
145155
{t('userselector.manageaccounts', `Manage accounts`)}
146156
</div>
@@ -150,6 +160,7 @@ const WalletDropdown: React.FC = observer(() => {
150160
await window.api.logOut()
151161
await invalidateQuery()
152162
}}
163+
data-tour="account-logout"
153164
>
154165
<div
155166
className={`body ${styles.itemContents} ${styles.logOut}`}
@@ -159,7 +170,10 @@ const WalletDropdown: React.FC = observer(() => {
159170
</Menu.Item>
160171
</>
161172
) : (
162-
<Menu.Item onClick={() => authState.openSignInModal()}>
173+
<Menu.Item
174+
onClick={() => authState.openSignInModal()}
175+
data-tour="account-login"
176+
>
163177
<div className={`body ${styles.itemContents}`}>
164178
{t('userselector.logIn', `Log in`)}
165179
</div>

src/frontend/components/UI/TopNavBar/index.module.scss

+16
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@
77
align-items: center;
88
padding: var(--space-md) var(--space-lg);
99
z-index: 100000;
10+
11+
.tourButton {
12+
margin-left: var(--space-2xs);
13+
color: var(--text-default);
14+
background-color: var(--transparent);
15+
border: none;
16+
17+
svg {
18+
width: 28px;
19+
height: 28px;
20+
}
21+
22+
&:hover {
23+
color: var(--text-hover);
24+
}
25+
}
1026
}
1127

1228
.navBar > :first-child {

src/frontend/components/UI/TopNavBar/index.tsx

+21-6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import {
1717
import webviewNavigationStore from 'frontend/store/WebviewNavigationStore'
1818
import { extractMainDomain } from '../../../helpers/extract-main-domain'
1919
import AppVersion from '../AppVersion'
20+
import { TourTriggerButton, TourGuide } from '../../TourGuide'
2021

2122
const TopNavBar = observer(() => {
2223
const { t } = useTranslation()
@@ -52,12 +53,13 @@ const TopNavBar = observer(() => {
5253
}
5354

5455
return (
55-
<div className={styles.navBar}>
56+
<div className={styles.navBar} data-tour="topnav-container">
5657
<div>
5758
<HyperPlayLogoWhite
5859
height="27px"
5960
width="27px"
6061
className={styles.hpLogo}
62+
data-tour="topnav-logo"
6163
/>
6264
<Images.HyperPlayTextLogo
6365
fill="var(--color-neutral-100)"
@@ -69,7 +71,7 @@ const TopNavBar = observer(() => {
6971
</div>
7072
</div>
7173
<>
72-
<NavLink to="/hyperplaystore">
74+
<NavLink to="/hyperplaystore" data-tour="topnav-hyperplay-store">
7375
<Button
7476
type="link"
7577
size="small"
@@ -78,7 +80,7 @@ const TopNavBar = observer(() => {
7880
HyperPlay
7981
</Button>
8082
</NavLink>
81-
<NavLink to="/epicstore">
83+
<NavLink to="/epicstore" data-tour="topnav-epic-store">
8284
<Button
8385
type="link"
8486
size="small"
@@ -87,7 +89,7 @@ const TopNavBar = observer(() => {
8789
{t('Epic Games', 'Epic Games')}
8890
</Button>
8991
</NavLink>
90-
<NavLink to="/gogstore">
92+
<NavLink to="/gogstore" data-tour="topnav-gog-store">
9193
<Button
9294
type="link"
9395
size="small"
@@ -99,22 +101,35 @@ const TopNavBar = observer(() => {
99101
</>
100102
</div>
101103
<div>
102-
{pathname === '/library' ? <SearchBar /> : null}
104+
{pathname === '/library' ? (
105+
<SearchBar data-tour="topnav-search" />
106+
) : null}
103107
{showMetaMaskBrowserSidebarLinks && (
104108
<button
105109
className={styles.iconButton}
106110
onClick={() => extensionStore.toggleIsPopupOpen()}
107111
onMouseEnter={() => extensionStore.lockPopup()}
108112
onMouseLeave={() => extensionStore.unlockPopup()}
113+
data-tour="topnav-metamask"
109114
>
110115
<Images.MetaMask fill="white" />
111116
{badgeText !== '' && badgeText !== '0' ? (
112117
<div className={styles.badge}>{badgeText}</div>
113118
) : null}
114119
</button>
115120
)}
116-
<AccountDropdown />
121+
<div data-tour="topnav-account">
122+
<AccountDropdown />
123+
</div>
124+
<TourTriggerButton
125+
tourId="topnav"
126+
className={styles.tourButton}
127+
buttonType="tertiary"
128+
showIcon={true}
129+
showText={false}
130+
/>
117131
</div>
132+
<TourGuide />
118133
</div>
119134
)
120135
})

0 commit comments

Comments
 (0)