@opexa/portal-components 0.1.56 → 0.1.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/dist/client/hooks/useAddFavoriteGameMutation.d.ts +2 -0
  2. package/dist/client/hooks/useAddFavoriteGameMutation.js +39 -0
  3. package/dist/client/hooks/useCreateGameSessionMutation.js +2 -2
  4. package/dist/client/hooks/useEndGameSessionMutation.js +1 -1
  5. package/dist/client/hooks/useFavoriteGamesQuery.d.ts +2 -2
  6. package/dist/client/hooks/useFavoriteGamesQuery.js +10 -3
  7. package/dist/client/hooks/useMarkGameAsFavoriteMutation.js +1 -9
  8. package/dist/client/hooks/useRecentGamesQuery.js +3 -11
  9. package/dist/client/hooks/useRemoveFavoriteGameMutation.d.ts +2 -0
  10. package/dist/client/hooks/useRemoveFavoriteGameMutation.js +27 -0
  11. package/dist/client/hooks/useUnmarkGameAsFavoriteMutation.js +1 -1
  12. package/dist/components/FavoriteGames/FavoriteGames.client.js +9 -8
  13. package/dist/components/GameLaunch/GameLaunch.lazy.js +11 -14
  14. package/dist/components/GameLaunch/GameLaunchTrigger.js +1 -1
  15. package/dist/components/Games/Game.js +13 -13
  16. package/dist/handlers/v4Cookies.d.ts +9 -0
  17. package/dist/handlers/v4Cookies.js +49 -0
  18. package/dist/server/utils/prefetchFavoriteGamesQuery.js +9 -2
  19. package/dist/services/portal.d.ts +23 -1
  20. package/dist/services/portal.js +25 -1
  21. package/dist/services/queries.d.ts +3 -3
  22. package/dist/services/queries.js +7 -12
  23. package/dist/services/wallet.d.ts +1 -23
  24. package/dist/services/wallet.js +1 -25
  25. package/dist/types/index.d.ts +0 -3
  26. package/dist/ui/AlertDialog/AlertDialog.d.ts +154 -154
  27. package/dist/ui/AlertDialog/alertDialog.recipe.d.ts +14 -14
  28. package/dist/ui/Checkbox/Checkbox.d.ts +23 -23
  29. package/dist/ui/Checkbox/checkbox.recipe.d.ts +3 -3
  30. package/dist/ui/Clipboard/Clipboard.d.ts +18 -18
  31. package/dist/ui/Clipboard/clipboard.recipe.d.ts +3 -3
  32. package/dist/ui/Collapsible/Collapsible.d.ts +20 -20
  33. package/dist/ui/Collapsible/collapsible.recipe.d.ts +5 -5
  34. package/dist/ui/Combobox/Combobox.d.ts +42 -42
  35. package/dist/ui/Combobox/combobox.recipe.d.ts +3 -3
  36. package/dist/ui/DatePicker/DatePicker.d.ts +72 -72
  37. package/dist/ui/DatePicker/datePicker.recipe.d.ts +3 -3
  38. package/dist/ui/Dialog/Dialog.d.ts +33 -33
  39. package/dist/ui/Dialog/dialog.recipe.d.ts +3 -3
  40. package/dist/ui/Drawer/Drawer.d.ts +33 -33
  41. package/dist/ui/Drawer/drawer.recipe.d.ts +3 -3
  42. package/dist/ui/Menu/Menu.d.ts +90 -90
  43. package/dist/ui/Menu/menu.recipe.d.ts +5 -5
  44. package/dist/ui/Popover/Popover.d.ts +88 -88
  45. package/dist/ui/Popover/popover.recipe.d.ts +8 -8
  46. package/dist/ui/Select/Select.d.ts +45 -45
  47. package/dist/ui/Select/select.recipe.d.ts +3 -3
  48. package/dist/ui/Table/Table.d.ts +21 -21
  49. package/dist/ui/Table/table.anatomy.d.ts +1 -1
  50. package/dist/ui/Table/table.recipe.d.ts +3 -3
  51. package/dist/ui/Tabs/Tabs.d.ts +15 -15
  52. package/dist/ui/Tabs/tabs.recipe.d.ts +3 -3
  53. package/dist/utils/get-fingerprint.d.ts +8 -0
  54. package/dist/utils/get-fingerprint.js +37 -0
  55. package/dist/utils/mutationKeys.d.ts +2 -2
  56. package/dist/utils/mutationKeys.js +4 -4
  57. package/package.json +1 -1
@@ -0,0 +1,2 @@
1
+ import type { Mutation } from '../../types';
2
+ export declare const useAddFavoriteGameMutation: Mutation<string, string>;
@@ -0,0 +1,39 @@
1
+ import { useMutation } from '@tanstack/react-query';
2
+ import invariant from 'tiny-invariant';
3
+ import { getSession } from '../../client/services/getSession.js';
4
+ import { addFavoriteGame } from '../../services/portal.js';
5
+ import { getQueryClient } from '../../utils/getQueryClient.js';
6
+ import { getAddFavoriteGameMutationKey } from '../../utils/mutationKeys.js';
7
+ import { getFavoriteGamesQueryKey, getGamesQueryKey, } from '../../utils/queryKeys.js';
8
+ export const useAddFavoriteGameMutation = (config) => {
9
+ const queryClient = getQueryClient();
10
+ const mutation = useMutation({
11
+ ...config,
12
+ mutationKey: getAddFavoriteGameMutationKey(),
13
+ mutationFn: async (id) => {
14
+ const session = await getSession();
15
+ invariant(session.status === 'authenticated');
16
+ await addFavoriteGame(id, {
17
+ headers: {
18
+ Authorization: `Bearer ${session.token}`,
19
+ },
20
+ });
21
+ const games = queryClient
22
+ .getQueriesData({
23
+ queryKey: getGamesQueryKey({}),
24
+ })
25
+ .map(([, arr]) => arr)
26
+ .filter(Boolean)
27
+ .flatMap((obj) => obj.pages.flatMap((q) => q.edges.map((edge) => edge.node)));
28
+ const game = games.find((game) => game.id === id);
29
+ queryClient.setQueryData(getFavoriteGamesQueryKey(), (prev = []) => {
30
+ if (game) {
31
+ return [...prev, game];
32
+ }
33
+ return prev;
34
+ });
35
+ return id;
36
+ },
37
+ });
38
+ return mutation;
39
+ };
@@ -16,10 +16,10 @@ export const useCreateGameSessionMutation = (config) => {
16
16
  const id = ObjectId.generate(ObjectType.GameSession).toString();
17
17
  const session = await getSession();
18
18
  invariant(session.status === 'authenticated');
19
- if (LEGACY_GAME_PROVIDERS.includes(game.provider)) {
19
+ if ('provider' in game && LEGACY_GAME_PROVIDERS.includes(game.provider)) {
20
20
  await createGameSession__legacy({
21
21
  id,
22
- game: 'reference' in game ? game.reference : game.id,
22
+ game: game.reference,
23
23
  homepageUrl: window.location.origin.includes('local')
24
24
  ? undefined
25
25
  : window.location.origin,
@@ -12,7 +12,7 @@ export const useEndGameSessionMutation = (config) => {
12
12
  mutationFn: async (input) => {
13
13
  const session = await getSession();
14
14
  invariant(session.status === 'authenticated');
15
- if (LEGACY_GAME_PROVIDERS.includes(input.game.provider)) {
15
+ if ('provider' in input.game && LEGACY_GAME_PROVIDERS.includes(input.game.provider)) {
16
16
  await endGameSession__legacy(input.session.id, {
17
17
  headers: {
18
18
  Authorization: `Bearer ${session.token}`,
@@ -1,2 +1,2 @@
1
- import type { FavoriteGame, Query } from '../../types';
2
- export declare const useFavoriteGamesQuery: Query<FavoriteGame[]>;
1
+ import type { Game, Query } from '../../types';
2
+ export declare const useFavoriteGamesQuery: Query<Game[]>;
@@ -1,6 +1,7 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
2
  import invariant from 'tiny-invariant';
3
- import { getFavoriteGames } from '../../services/wallet.js';
3
+ import { getGamesv2__next } from '../../services/cmsPortal.js';
4
+ import { getFavoriteGames } from '../../services/portal.js';
4
5
  import { getQueryClient } from '../../utils/getQueryClient.js';
5
6
  import { getFavoriteGamesQueryKey, getSessionQueryKey, } from '../../utils/queryKeys.js';
6
7
  import { getSession } from '../services/getSession.js';
@@ -22,13 +23,19 @@ export const useFavoriteGamesQuery = (config) => {
22
23
  queryFn: async () => getSession(),
23
24
  });
24
25
  invariant(session.status === 'authenticated');
25
- const favoriteGames = getFavoriteGames({
26
+ const favoriteGames = await getFavoriteGames({
26
27
  signal,
27
28
  headers: {
28
29
  Authorization: `Bearer ${session.token}`,
29
30
  },
30
31
  });
31
- return favoriteGames ?? [];
32
+ const ids = favoriteGames?.length
33
+ ? [...new Set(favoriteGames.map((g) => g.id))]
34
+ : [];
35
+ if (ids.length <= 0)
36
+ return [];
37
+ const results = await Promise.all(ids.map((id) => getGamesv2__next(id, { signal })));
38
+ return results.flatMap((res) => res.edges.map((edge) => edge.node));
32
39
  },
33
40
  });
34
41
  };
@@ -28,15 +28,7 @@ export const useMarkGameAsFavoriteMutation = (config) => {
28
28
  const game = games.find((game) => game.reference === id);
29
29
  queryClient.setQueryData(getFavoriteGamesQueryKey(), (prev = []) => {
30
30
  if (game) {
31
- return [
32
- ...prev,
33
- {
34
- id: game.reference,
35
- name: game.name,
36
- type: game.type,
37
- provider: game.provider,
38
- },
39
- ];
31
+ return [...prev, game];
40
32
  }
41
33
  });
42
34
  return id;
@@ -1,6 +1,6 @@
1
1
  import { useQuery } from '@tanstack/react-query';
2
2
  import invariant from 'tiny-invariant';
3
- import { getGames__next } from '../../services/cmsPortal.js';
3
+ import { getGamesv2__next } from '../../services/cmsPortal.js';
4
4
  import { getRecentGames } from '../../services/portal.js';
5
5
  import { getQueryClient } from '../../utils/getQueryClient.js';
6
6
  import { getRecentGamesQueryKey, getSessionQueryKey, } from '../../utils/queryKeys.js';
@@ -34,16 +34,8 @@ export const useRecentGamesQuery = (config) => {
34
34
  : [];
35
35
  if (ids.length <= 0)
36
36
  return [];
37
- const res = await getGames__next({
38
- filter: {
39
- reference: {
40
- in: ids,
41
- },
42
- },
43
- }, { signal });
44
- return ids
45
- .map((id) => res.edges.find((e) => id === e.node.reference)?.node ?? null)
46
- .filter((g) => g !== null);
37
+ const results = await Promise.all(ids.map((id) => getGamesv2__next(id, { signal })));
38
+ return results.flatMap((res) => res.edges.map((edge) => edge.node));
47
39
  },
48
40
  });
49
41
  };
@@ -0,0 +1,2 @@
1
+ import type { Mutation } from '../../types';
2
+ export declare const useRemoveFavoriteGameMutation: Mutation<void, string>;
@@ -0,0 +1,27 @@
1
+ import { useMutation } from '@tanstack/react-query';
2
+ import invariant from 'tiny-invariant';
3
+ import { removeFavoriteGame } from '../../services/portal.js';
4
+ import { getQueryClient } from '../../utils/getQueryClient.js';
5
+ import { getRemoveFavoriteGameMutationKey } from '../../utils/mutationKeys.js';
6
+ import { getFavoriteGamesQueryKey } from '../../utils/queryKeys.js';
7
+ import { getSession } from '../services/getSession.js';
8
+ export const useRemoveFavoriteGameMutation = (config) => {
9
+ const queryClient = getQueryClient();
10
+ const mutation = useMutation({
11
+ ...config,
12
+ mutationKey: getRemoveFavoriteGameMutationKey(),
13
+ mutationFn: async (id) => {
14
+ const session = await getSession();
15
+ invariant(session.status === 'authenticated');
16
+ await removeFavoriteGame(id, {
17
+ headers: {
18
+ Authorization: `Bearer ${session.token}`,
19
+ },
20
+ });
21
+ queryClient.setQueryData(getFavoriteGamesQueryKey(), (prev) => {
22
+ return prev?.filter((game) => game.id !== id);
23
+ });
24
+ },
25
+ });
26
+ return mutation;
27
+ };
@@ -19,7 +19,7 @@ export const useUnmarkGameAsFavoriteMutation = (config) => {
19
19
  },
20
20
  });
21
21
  queryClient.setQueryData(getFavoriteGamesQueryKey(), (prev) => {
22
- return prev?.filter((game) => game.id !== id);
22
+ return prev?.filter((game) => game.reference !== id);
23
23
  });
24
24
  },
25
25
  });
@@ -9,8 +9,8 @@ import { useAccountQuery } from '../../client/hooks/useAccountQuery.js';
9
9
  import { useBypassKycChecker, } from '../../client/hooks/useBypassKycChecker.js';
10
10
  import { useFavoriteGamesQuery } from '../../client/hooks/useFavoriteGamesQuery.js';
11
11
  import { useFeatureFlag } from '../../client/hooks/useFeatureFlag.js';
12
+ import { useRemoveFavoriteGameMutation } from '../../client/hooks/useRemoveFavoriteGameMutation.js';
12
13
  import { useSessionQuery } from '../../client/hooks/useSessionQuery.js';
13
- import { useUnmarkGameAsFavoriteMutation } from '../../client/hooks/useUnmarkGameAsFavoriteMutation.js';
14
14
  import { toaster } from '../../client/utils/toaster.js';
15
15
  import { ArrowLeftIcon } from '../../icons/ArrowLeftIcon.js';
16
16
  import { ArrowRightIcon } from '../../icons/ArrowRightIcon.js';
@@ -63,7 +63,7 @@ function Item({ game, bypassKycCheck, className, }) {
63
63
  const session = sessionQuery.data;
64
64
  const accountQuery = useAccountQuery();
65
65
  const account = accountQuery.data;
66
- const unmarkGameAsFavoriteMutation = useUnmarkGameAsFavoriteMutation({
66
+ const removeFavoriteGameMutation = useRemoveFavoriteGameMutation({
67
67
  onSuccess() {
68
68
  toaster.success({
69
69
  description: 'Game removed from favorites',
@@ -75,19 +75,20 @@ function Item({ game, bypassKycCheck, className, }) {
75
75
  });
76
76
  },
77
77
  });
78
- const markUnmarkGameAsFavoriteEnabled = session?.status !== 'authenticated'
78
+ const addRemoveFavoriteGameEnabled = session?.status !== 'authenticated'
79
79
  ? false
80
80
  : featureFlag.enabled
81
81
  ? account?.status !== 'VERIFICATION_LOCKED'
82
82
  : true;
83
- return (_jsxs(GameLaunchTrigger, { bypassKycCheck: bypassKycCheck, game: game, className: twMerge('hover:-translate-y-1 relative block w-full animate-scale-in shadow-sm transition-transform duration-200', className?.thumbnailRoot), children: [markUnmarkGameAsFavoriteEnabled && (_jsx("div", { role: "button", tabIndex: 0, onClick: (e) => {
83
+ return (_jsxs(GameLaunchTrigger, { bypassKycCheck: bypassKycCheck, game: game, className: twMerge('hover:-translate-y-1 relative block w-full animate-scale-in shadow-sm transition-transform duration-200', className?.thumbnailRoot), children: [addRemoveFavoriteGameEnabled && (_jsx("div", { role: "button", tabIndex: 0, onClick: (e) => {
84
84
  e.preventDefault();
85
85
  e.stopPropagation();
86
- if (unmarkGameAsFavoriteMutation.isPending)
86
+ if (removeFavoriteGameMutation.isPending)
87
87
  return;
88
- unmarkGameAsFavoriteMutation.mutate(game.id);
89
- }, "aria-disabled": unmarkGameAsFavoriteMutation.isPending, className: "absolute top-2 right-2 flex size-9 items-center justify-center rounded-xs bg-black/65 blur-[0.5px]", children: _jsx(Star01Icon, { className: "size-5 fill-yellow-400 text-yellow-400" }) })), _jsx(Image, { src: getGameImageUrl({
90
- id: game.id,
88
+ removeFavoriteGameMutation.mutate(game.id);
89
+ }, "aria-disabled": removeFavoriteGameMutation.isPending, className: "absolute top-2 right-2 flex size-9 items-center justify-center rounded-xs bg-black/65 blur-[0.5px]", children: _jsx(Star01Icon, { className: "size-5 fill-yellow-400 text-yellow-400" }) })), _jsx(Image, { src: getGameImageUrl({
90
+ reference: game.reference,
91
91
  provider: game.provider,
92
+ image: game.image,
92
93
  }), alt: "", width: 200, height: 200, loading: "lazy", unoptimized: true, className: "aspect-square w-full rounded-t-md object-cover" }), _jsx("span", { className: twMerge('block w-full truncate rounded-b-md bg-bg-tertiary px-2 py-3.5 text-center font-semibold text-text-primary-brand text-xs', className?.thumbnailTitle), children: game.name })] }));
93
94
  }
@@ -3,12 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
3
3
  import { Dialog } from '@ark-ui/react/dialog';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
  import { useShallow } from 'zustand/shallow';
6
+ import { useAddFavoriteGameMutation } from '../../client/hooks/useAddFavoriteGameMutation.js';
6
7
  import { useDisclosure } from '../../client/hooks/useDisclosure.js';
7
8
  import { useEndGameSessionMutation } from '../../client/hooks/useEndGameSessionMutation.js';
8
9
  import { useFavoriteGamesQuery } from '../../client/hooks/useFavoriteGamesQuery.js';
9
10
  import { useGlobalStore } from '../../client/hooks/useGlobalStore.js';
10
- import { useMarkGameAsFavoriteMutation } from '../../client/hooks/useMarkGameAsFavoriteMutation.js';
11
- import { useUnmarkGameAsFavoriteMutation } from '../../client/hooks/useUnmarkGameAsFavoriteMutation.js';
11
+ import { useRemoveFavoriteGameMutation } from '../../client/hooks/useRemoveFavoriteGameMutation.js';
12
12
  import { toaster } from '../../client/utils/toaster.js';
13
13
  import { AlertCircleIcon } from '../../icons/AlertCircleIcon.js';
14
14
  import { ChevronDownIcon } from '../../icons/ChevronDownIcon.js';
@@ -48,7 +48,7 @@ export function GameLaunch({ locale = 'local' }) {
48
48
  const favoriteGamesQuery = useFavoriteGamesQuery();
49
49
  const favoriteGames = favoriteGamesQuery.data ?? [];
50
50
  const endGameSessionMutation = useEndGameSessionMutation();
51
- const markGameAsFavoriteMutation = useMarkGameAsFavoriteMutation({
51
+ const addFavoriteGameMutation = useAddFavoriteGameMutation({
52
52
  onSuccess() {
53
53
  toaster.success({
54
54
  description: 'Game added to favorites',
@@ -60,7 +60,7 @@ export function GameLaunch({ locale = 'local' }) {
60
60
  });
61
61
  },
62
62
  });
63
- const unmarkGameAsFavoriteMutation = useUnmarkGameAsFavoriteMutation({
63
+ const removeFavoriteGameMutation = useRemoveFavoriteGameMutation({
64
64
  onSuccess() {
65
65
  toaster.success({
66
66
  description: 'Game removed from favorites',
@@ -76,8 +76,9 @@ export function GameLaunch({ locale = 'local' }) {
76
76
  return !globalStore.gameLaunch.details.game
77
77
  ? false
78
78
  : 'reference' in globalStore.gameLaunch.details.game
79
- ? favoriteGame.id === globalStore.gameLaunch.details.game.reference
80
- : favoriteGame.id === globalStore.gameLaunch.details.game.id;
79
+ ? favoriteGame.reference ===
80
+ globalStore.gameLaunch.details.game.reference
81
+ : favoriteGame.reference === globalStore.gameLaunch.details.game.id;
81
82
  });
82
83
  return (_jsxs(_Fragment, { children: [_jsx(Dialog.Root, { open: globalStore.gameLaunch.details.status !== 'WAITING' &&
83
84
  !cancelDisclosure.open, lazyMount: true, closeOnEscape: false, closeOnInteractOutside: false, children: _jsx(Portal, { children: _jsx(Dialog.Positioner, { className: "fixed inset-0 z-dialog", children: _jsxs(Dialog.Content, { className: "relative size-full ui-closed:animate-dialog-out ui-open:animate-dialog-in overflow-y-auto bg-bg-primary-alt", children: [_jsx(Presence, { asChild: true, lazyMount: true, unmountOnExit: true, present: !maximizeDisclosure.open, children: _jsxs("section", { className: "absolute top-safe-area-inset-top left-0 z-1 flex h-[3.25rem] w-full ui-closed:animate-slide-out-down ui-open:animate-slide-in-down items-center gap-2 bg-bg-primary-alt px-4 py-2", children: [_jsx(IconButton, { size: "xs", variant: "outline", onClick: () => {
@@ -86,19 +87,15 @@ export function GameLaunch({ locale = 'local' }) {
86
87
  return;
87
88
  }
88
89
  cancelDisclosure.setOpen(true);
89
- }, disabled: globalStore.gameLaunch.details.status === 'LOADING', children: _jsx(ChevronLeftIcon, { className: "size-5" }) }), _jsx("div", { className: "grow" }), _jsx(IconButton, { size: "xs", variant: "outline", disabled: unmarkGameAsFavoriteMutation.isPending ||
90
- markGameAsFavoriteMutation.isPending, onClick: () => {
90
+ }, disabled: globalStore.gameLaunch.details.status === 'LOADING', children: _jsx(ChevronLeftIcon, { className: "size-5" }) }), _jsx("div", { className: "grow" }), _jsx(IconButton, { size: "xs", variant: "outline", disabled: removeFavoriteGameMutation.isPending ||
91
+ addFavoriteGameMutation.isPending, onClick: () => {
91
92
  if (!globalStore.gameLaunch.details.game)
92
93
  return;
93
94
  if (markedAsFavorite) {
94
- unmarkGameAsFavoriteMutation.mutate('reference' in globalStore.gameLaunch.details.game
95
- ? globalStore.gameLaunch.details.game.reference
96
- : globalStore.gameLaunch.details.game.id);
95
+ removeFavoriteGameMutation.mutate(globalStore.gameLaunch.details.game.id);
97
96
  }
98
97
  else {
99
- markGameAsFavoriteMutation.mutate('reference' in globalStore.gameLaunch.details.game
100
- ? globalStore.gameLaunch.details.game.reference
101
- : globalStore.gameLaunch.details.game.id);
98
+ addFavoriteGameMutation.mutate(globalStore.gameLaunch.details.game.id);
102
99
  }
103
100
  }, children: _jsx(Star01Icon, { className: twMerge('size-5', markedAsFavorite
104
101
  ? 'fill-yellow-400 text-yellow-400'
@@ -51,7 +51,7 @@ export function GameLaunchTrigger(props) {
51
51
  : globalStore.gameLaunch.details.status !== 'WAITING'
52
52
  ? true
53
53
  : props.disabled;
54
- return (_jsx(ark.button, { type: "button", "aria-label": `Play ${game.name}`, "data-state": sessionQuery.data?.status === 'unauthenticated'
54
+ return (_jsx(ark.button, { type: "button", "aria-label": `Play ${'name' in game ? game.name : game.id}`, "data-state": sessionQuery.data?.status === 'unauthenticated'
55
55
  ? globalStore.signIn.open
56
56
  ? 'open'
57
57
  : 'closed'
@@ -4,11 +4,11 @@ import Image, {} from 'next/image';
4
4
  import { useMemo, useState } from 'react';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { useAccountQuery } from '../../client/hooks/useAccountQuery.js';
7
+ import { useAddFavoriteGameMutation } from '../../client/hooks/useAddFavoriteGameMutation.js';
7
8
  import { useFavoriteGamesQuery } from '../../client/hooks/useFavoriteGamesQuery.js';
8
9
  import { useFeatureFlag } from '../../client/hooks/useFeatureFlag.js';
9
- import { useMarkGameAsFavoriteMutation } from '../../client/hooks/useMarkGameAsFavoriteMutation.js';
10
+ import { useRemoveFavoriteGameMutation } from '../../client/hooks/useRemoveFavoriteGameMutation.js';
10
11
  import { useSessionQuery } from '../../client/hooks/useSessionQuery.js';
11
- import { useUnmarkGameAsFavoriteMutation } from '../../client/hooks/useUnmarkGameAsFavoriteMutation.js';
12
12
  import { toaster } from '../../client/utils/toaster.js';
13
13
  import { Star01Icon } from '../../icons/Star01Icon.js';
14
14
  import RainbowballImg from '../../images/rainbow-ball-online.webp';
@@ -27,7 +27,7 @@ export function Game(props) {
27
27
  const account = accountQuery.data;
28
28
  const favoriteGamesQuery = useFavoriteGamesQuery();
29
29
  const favoriteGames = favoriteGamesQuery.data ?? [];
30
- const markGameAsFavoriteMutation = useMarkGameAsFavoriteMutation({
30
+ const addFavoriteGameMutation = useAddFavoriteGameMutation({
31
31
  onSuccess() {
32
32
  toaster.success({
33
33
  description: 'Game added to favorites',
@@ -39,7 +39,7 @@ export function Game(props) {
39
39
  });
40
40
  },
41
41
  });
42
- const unmarkGameAsFavoriteMutation = useUnmarkGameAsFavoriteMutation({
42
+ const removeFavoriteGameMutation = useRemoveFavoriteGameMutation({
43
43
  onSuccess() {
44
44
  toaster.success({
45
45
  description: 'Game removed from favorites',
@@ -51,32 +51,32 @@ export function Game(props) {
51
51
  });
52
52
  },
53
53
  });
54
- const markedAsFavorite = favoriteGames.some((favoriteGame) => favoriteGame.id === game.reference);
54
+ const markedAsFavorite = favoriteGames.some((favoriteGame) => favoriteGame.reference === game.reference);
55
55
  const classNames = isString(props.className)
56
56
  ? { root: props.className }
57
57
  : (props.className ?? {});
58
- const markUnmarkGameAsFavoriteEnabled = session?.status !== 'authenticated'
58
+ const addRemoveFavoriteGameEnabled = session?.status !== 'authenticated'
59
59
  ? false
60
60
  : featureFlag.enabled
61
61
  ? account?.status !== 'VERIFICATION_LOCKED'
62
62
  : true;
63
- return (_jsxs(GameLaunchTrigger, { bypassKycCheck: props.bypassKycCheck, game: game, className: twMerge('group md:hover:-translate-y-1 relative flex h-full w-full flex-col shadow-sm transition-transform duration-200', classNames.root), "aria-label": `Play ${game.name} game`, children: [props.badge === 'new' && (_jsx(BadgeNew, { className: "absolute top-0 left-0 size-[3.75rem]" })), props.badge === 'top' && (_jsx(BadgeTop, { className: "absolute top-0 left-0 size-[3.75rem]" })), props.badge === 'popular' && (_jsx(BadgePopular, { className: "absolute top-0 left-0 size-[3.75rem]" })), markUnmarkGameAsFavoriteEnabled && (_jsx("div", { role: "button", tabIndex: 0, onClick: (e) => {
63
+ return (_jsxs(GameLaunchTrigger, { bypassKycCheck: props.bypassKycCheck, game: game, className: twMerge('group md:hover:-translate-y-1 relative flex h-full w-full flex-col shadow-sm transition-transform duration-200', classNames.root), "aria-label": `Play ${game.name} game`, children: [props.badge === 'new' && (_jsx(BadgeNew, { className: "absolute top-0 left-0 size-[3.75rem]" })), props.badge === 'top' && (_jsx(BadgeTop, { className: "absolute top-0 left-0 size-[3.75rem]" })), props.badge === 'popular' && (_jsx(BadgePopular, { className: "absolute top-0 left-0 size-[3.75rem]" })), addRemoveFavoriteGameEnabled && (_jsx("div", { role: "button", tabIndex: 0, onClick: (e) => {
64
64
  e.preventDefault();
65
65
  e.stopPropagation();
66
- if (markGameAsFavoriteMutation.isPending)
66
+ if (addFavoriteGameMutation.isPending)
67
67
  return;
68
- if (unmarkGameAsFavoriteMutation.isPending)
68
+ if (removeFavoriteGameMutation.isPending)
69
69
  return;
70
70
  if (markedAsFavorite) {
71
- unmarkGameAsFavoriteMutation.mutate(game.reference);
71
+ removeFavoriteGameMutation.mutate(game.id);
72
72
  }
73
73
  else {
74
- markGameAsFavoriteMutation.mutate(game.reference);
74
+ addFavoriteGameMutation.mutate(game.id);
75
75
  }
76
76
  }, "aria-label": markedAsFavorite
77
77
  ? 'Unmark as favorite game'
78
- : 'Mark as favorite game', "aria-disabled": markGameAsFavoriteMutation.isPending ||
79
- unmarkGameAsFavoriteMutation.isPending, className: "absolute top-2 right-2 flex size-9 items-center justify-center rounded-xs bg-black/65 opacity-100 blur-[0.5px] transition-opacity duration-200 md:opacity-0 md:group-hover:opacity-100", children: _jsx(Star01Icon, { className: twMerge('size-5 text-white transition-colors duration-200 hover:text-yellow-400', markedAsFavorite
78
+ : 'Mark as favorite game', "aria-disabled": addFavoriteGameMutation.isPending ||
79
+ removeFavoriteGameMutation.isPending, className: "absolute top-2 right-2 flex size-9 items-center justify-center rounded-xs bg-black/65 opacity-100 blur-[0.5px] transition-opacity duration-200 md:opacity-0 md:group-hover:opacity-100", children: _jsx(Star01Icon, { className: twMerge('size-5 text-white transition-colors duration-200 hover:text-yellow-400', markedAsFavorite
80
80
  ? 'fill-yellow-400 text-yellow-400'
81
81
  : 'text-white') }) })), _jsx(GameImage, { priority: props.priority }), _jsx("span", { className: twMerge('flex w-full flex-1 items-center justify-center break-words rounded-b-md bg-bg-tertiary px-2 py-3.5 text-center font-semibold text-text-primary-brand text-xs', classNames.title), children: game.name })] }));
82
82
  }
@@ -0,0 +1,9 @@
1
+ import type { NextResponse } from 'next/server';
2
+ /**
3
+ * Proxies AUTH `Set-Cookie` headers (the v4 refresh cookie) onto the outgoing
4
+ * Next.js response. In the proxy model the browser never talks to AUTH
5
+ * directly, so the cookie is re-scoped to the app domain (the `Domain`
6
+ * attribute from AUTH is dropped) and the route handler forwards it back to
7
+ * AUTH on subsequent requests via the `Cookie` header.
8
+ */
9
+ export declare function applyV4SetCookie(response: NextResponse, setCookie: string[]): void;
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Proxies AUTH `Set-Cookie` headers (the v4 refresh cookie) onto the outgoing
3
+ * Next.js response. In the proxy model the browser never talks to AUTH
4
+ * directly, so the cookie is re-scoped to the app domain (the `Domain`
5
+ * attribute from AUTH is dropped) and the route handler forwards it back to
6
+ * AUTH on subsequent requests via the `Cookie` header.
7
+ */
8
+ export function applyV4SetCookie(response, setCookie) {
9
+ for (const raw of setCookie) {
10
+ const parsed = parseSetCookie(raw);
11
+ if (!parsed)
12
+ continue;
13
+ response.cookies.set(parsed.name, parsed.value, parsed.options);
14
+ }
15
+ }
16
+ function parseSetCookie(raw) {
17
+ const [pair, ...attrs] = raw.split(';');
18
+ const eq = pair.indexOf('=');
19
+ if (eq < 0)
20
+ return null;
21
+ const name = pair.slice(0, eq).trim();
22
+ const value = pair.slice(eq + 1).trim();
23
+ const options = {};
24
+ for (const attr of attrs) {
25
+ const [k, v] = attr.split('=').map((s) => s.trim());
26
+ const key = k.toLowerCase();
27
+ if (key === 'path')
28
+ options.path = v;
29
+ else if (key === 'max-age')
30
+ options.maxAge = Number(v);
31
+ else if (key === 'expires') {
32
+ const d = new Date(v);
33
+ if (!Number.isNaN(d.getTime()))
34
+ options.expires = d;
35
+ }
36
+ else if (key === 'httponly')
37
+ options.httpOnly = true;
38
+ else if (key === 'secure')
39
+ options.secure = true;
40
+ else if (key === 'samesite') {
41
+ const lv = (v ?? '').toLowerCase();
42
+ if (lv === 'strict' || lv === 'lax' || lv === 'none') {
43
+ options.sameSite = lv;
44
+ }
45
+ }
46
+ // `Domain` is intentionally dropped so the cookie scopes to the app.
47
+ }
48
+ return { name, value, options };
49
+ }
@@ -1,5 +1,6 @@
1
1
  import { cache } from 'react';
2
- import { getFavoriteGames } from '../../services/wallet.js';
2
+ import { getGamesv2__next } from '../../services/cmsPortal.js';
3
+ import { getFavoriteGames } from '../../services/portal.js';
3
4
  import { getQueryClient } from '../../utils/getQueryClient.js';
4
5
  import { log } from '../../utils/log.js';
5
6
  import { getFavoriteGamesQueryKey } from '../../utils/queryKeys.js';
@@ -18,7 +19,13 @@ export const prefetchFavoriteGamesQuery = cache(async () => {
18
19
  Authorization: `Bearer ${session.token}`,
19
20
  },
20
21
  });
21
- return favoriteGames ?? [];
22
+ const ids = favoriteGames?.length
23
+ ? [...new Set(favoriteGames.map((g) => g.id))]
24
+ : [];
25
+ if (ids.length <= 0)
26
+ return [];
27
+ const results = await Promise.all(ids.map((id) => getGamesv2__next(id, { signal })));
28
+ return results.flatMap((res) => res.edges.map((edge) => edge.node));
22
29
  },
23
30
  })
24
31
  .catch(() => log.text("'prefetchFavoriteGamesQuery' failed", 'error'));
@@ -1,5 +1,5 @@
1
1
  import type { Simplify } from 'type-fest';
2
- import type { GameType, OnboardingPlayerExperience, OnboardingStatus, Platform, RecentGame, RecommendedGame, TopWin } from '../types';
2
+ import type { FavoriteGame, GameType, OnboardingPlayerExperience, OnboardingStatus, Platform, RecentGame, RecommendedGame, TopWin } from '../types';
3
3
  import { type GraphQLRequestOptions } from './graphqlRequest';
4
4
  export interface PlatformQuery extends Platform {
5
5
  }
@@ -12,6 +12,28 @@ export interface RecentGamesQuery {
12
12
  recentGames: RecentGame[];
13
13
  }
14
14
  export declare const getRecentGames: (options?: GraphQLRequestOptions) => Promise<RecentGame[]>;
15
+ export interface FavoriteGamesQuery {
16
+ favoriteGames: FavoriteGame[];
17
+ }
18
+ export declare const getFavoriteGames: (options?: GraphQLRequestOptions) => Promise<FavoriteGame[]>;
19
+ export interface AddFavoriteGameMutation {
20
+ addFavoriteGame: boolean;
21
+ }
22
+ export interface AddFavoriteGameMutationVariables {
23
+ input: {
24
+ game: string;
25
+ };
26
+ }
27
+ export declare const addFavoriteGame: (id: string, options?: GraphQLRequestOptions) => Promise<void>;
28
+ export interface RemoveFavoriteGameMutation {
29
+ removeFavoriteGame: boolean;
30
+ }
31
+ export interface RemoveFavoriteGameMutationVariables {
32
+ input: {
33
+ game: string;
34
+ };
35
+ }
36
+ export declare const removeFavoriteGame: (id: string, options?: GraphQLRequestOptions) => Promise<void>;
15
37
  export interface OnboardingStatusQuery {
16
38
  onboardingStatus?: OnboardingStatus | null;
17
39
  }
@@ -1,7 +1,7 @@
1
1
  import { cache } from 'react';
2
2
  import { PORTAL_GRAPHQL_ENDPOINT } from '../constants/index.js';
3
3
  import { graphqlRequest } from './graphqlRequest.js';
4
- import { COMPLETE_ONBOARDING, ONBOARDING_STATUS, PLATFORM, RECENT_GAMES, RECOMMENDED_GAMES, SKIP_ONBOARDING, TOP_WINS, } from './queries.js';
4
+ import { ADD_FAVORITE_GAME, COMPLETE_ONBOARDING, FAVORITE_GAMES, ONBOARDING_STATUS, PLATFORM, RECENT_GAMES, RECOMMENDED_GAMES, REMOVE_FAVORITE_GAME, SKIP_ONBOARDING, TOP_WINS, } from './queries.js';
5
5
  export const getPlatform = cache(async (options) => {
6
6
  const res = await graphqlRequest(PORTAL_GRAPHQL_ENDPOINT, PLATFORM, undefined, {
7
7
  cache: 'force-cache',
@@ -22,6 +22,30 @@ export const getRecentGames = cache(async (options) => {
22
22
  const res = await graphqlRequest(PORTAL_GRAPHQL_ENDPOINT, RECENT_GAMES, undefined, options);
23
23
  return res.recentGames;
24
24
  });
25
+ export const getFavoriteGames = cache(async (options) => {
26
+ const res = await graphqlRequest(PORTAL_GRAPHQL_ENDPOINT, FAVORITE_GAMES, undefined, options);
27
+ return res.favoriteGames;
28
+ });
29
+ export const addFavoriteGame = async (id, options) => {
30
+ const res = await graphqlRequest(PORTAL_GRAPHQL_ENDPOINT, ADD_FAVORITE_GAME, { input: { game: id } }, options);
31
+ if (!res.addFavoriteGame) {
32
+ const error = new Error();
33
+ error.name = 'UnknownError';
34
+ error.message = 'Something went wrong';
35
+ Error.captureStackTrace?.(error, addFavoriteGame);
36
+ throw error;
37
+ }
38
+ };
39
+ export const removeFavoriteGame = async (id, options) => {
40
+ const res = await graphqlRequest(PORTAL_GRAPHQL_ENDPOINT, REMOVE_FAVORITE_GAME, { input: { game: id } }, options);
41
+ if (!res.removeFavoriteGame) {
42
+ const error = new Error();
43
+ error.name = 'UnknownError';
44
+ error.message = 'Something went wrong';
45
+ Error.captureStackTrace?.(error, removeFavoriteGame);
46
+ throw error;
47
+ }
48
+ };
25
49
  export const getOnboardingStatus = cache(async (options) => {
26
50
  const res = await graphqlRequest(PORTAL_GRAPHQL_ENDPOINT, ONBOARDING_STATUS, undefined, options);
27
51
  return res.onboardingStatus ?? null;
@@ -138,9 +138,9 @@ export declare const REGISTER_FCM_DEVICE = "\n mutation RegisterFCMDevice($inpu
138
138
  export declare const UNREGISTER_FCM_DEVICE = "\n mutation UnregisterFCMDevice($input: UnregisterFCMDeviceInput!) {\n unregisterFCMDevice(input: $input)\n }\n";
139
139
  export declare const LINK_FIREBASE_CLOUD_MESSAGING_DEVICE = "\n mutation LinkFirebaseCloudMessagingDevice(\n $input: LinkFirebaseCloudMessagingDeviceInput!\n ) {\n linkFirebaseCloudMessagingDevice(input: $input)\n }\n";
140
140
  export declare const UNLINK_FIREBASE_CLOUD_MESSAGING_DEVICE = "\n mutation UnlinkFirebaseCloudMessagingDevice(\n $input: UnlinkFirebaseCloudMessagingDeviceInput!\n ) {\n unlinkFirebaseCloudMessagingDevice(input: $input)\n }\n";
141
- export declare const MARK_GAME_AS_FAVORITE = "\n mutation MarkGameAsFavorite($input: MarkGameAsFavoriteInput!) {\n markGameAsFavorite(input: $input)\n }\n";
142
- export declare const UNMARK_GAME_AS_FAVORITE = "\n mutation UnmarkGameAsFavorite($input: UnmarkGameAsFavoriteInput!) {\n unmarkGameAsFavorite(input: $input)\n }\n";
143
- export declare const FAVORITE_GAMES = "\n query FavoriteGames {\n favoriteGames {\n ... on Game {\n id\n name\n type\n provider\n }\n }\n }\n";
141
+ export declare const ADD_FAVORITE_GAME = "\n mutation AddFavoriteGame($input: AddFavoriteGameInput!) {\n addFavoriteGame(input: $input)\n }\n";
142
+ export declare const REMOVE_FAVORITE_GAME = "\n mutation RemoveFavoriteGame($input: RemoveFavoriteGameInput!) {\n removeFavoriteGame(input: $input)\n }\n";
143
+ export declare const FAVORITE_GAMES = "\n query FavoriteGames {\n favoriteGames {\n id\n }\n }\n";
144
144
  export declare const PLATFORM_GAMES = "\n query PlatformGames($first: Int, $after: Cursor, $filter: _GameFilterInput) {\n _games(first: $first, after: $after, filter: $filter) {\n totalCount\n pageInfo {\n hasNextPage\n endCursor\n }\n edges {\n cursor\n node {\n ... on _Game {\n id\n reference\n name\n displayName\n type\n provider\n status\n hidden\n hasFreeBet\n hasJackpot\n }\n }\n }\n }\n }\n";
145
145
  export declare const GOOGLE_CLIENT_ID = "\n query GoogleClientId {\n googleClientId\n }\n";
146
146
  export declare const FACEBOOK_CLIENT_ID = "\n query FacebookClientId {\n facebookClientId\n }\n";
@@ -3378,25 +3378,20 @@ export const UNLINK_FIREBASE_CLOUD_MESSAGING_DEVICE = /* GraphQL */ `
3378
3378
  unlinkFirebaseCloudMessagingDevice(input: $input)
3379
3379
  }
3380
3380
  `;
3381
- export const MARK_GAME_AS_FAVORITE = /* GraphQL */ `
3382
- mutation MarkGameAsFavorite($input: MarkGameAsFavoriteInput!) {
3383
- markGameAsFavorite(input: $input)
3381
+ export const ADD_FAVORITE_GAME = /* GraphQL */ `
3382
+ mutation AddFavoriteGame($input: AddFavoriteGameInput!) {
3383
+ addFavoriteGame(input: $input)
3384
3384
  }
3385
3385
  `;
3386
- export const UNMARK_GAME_AS_FAVORITE = /* GraphQL */ `
3387
- mutation UnmarkGameAsFavorite($input: UnmarkGameAsFavoriteInput!) {
3388
- unmarkGameAsFavorite(input: $input)
3386
+ export const REMOVE_FAVORITE_GAME = /* GraphQL */ `
3387
+ mutation RemoveFavoriteGame($input: RemoveFavoriteGameInput!) {
3388
+ removeFavoriteGame(input: $input)
3389
3389
  }
3390
3390
  `;
3391
3391
  export const FAVORITE_GAMES = /* GraphQL */ `
3392
3392
  query FavoriteGames {
3393
3393
  favoriteGames {
3394
- ... on Game {
3395
- id
3396
- name
3397
- type
3398
- provider
3399
- }
3394
+ id
3400
3395
  }
3401
3396
  }
3402
3397
  `;