@maestro_io/maestro-web-sdk 3.3.1 → 4.0.0-beta.2

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 (269) hide show
  1. package/dist/f8820fca3a2a7957f753167da0884f9d.png +0 -0
  2. package/dist/maestro-web-sdk.esm.js +12 -12
  3. package/dist/maestro-web-sdk.esm.js.map +1 -1
  4. package/dist/maestro-web-sdk.umd.js +12 -12
  5. package/dist/maestro-web-sdk.umd.js.map +1 -1
  6. package/dist/package.json +87 -0
  7. package/dist/src/__mocks__/sentry.d.ts +4 -0
  8. package/dist/src/components/atoms/BaseButton/BaseButton.js +11 -0
  9. package/dist/src/components/atoms/BaseButton/index.js +2 -0
  10. package/dist/src/components/atoms/Image/index.js +34 -0
  11. package/dist/src/components/atoms/Rive/index.js +53 -0
  12. package/dist/src/components/atoms/SvgIcon/BetGeneralIcon.js +9 -0
  13. package/dist/src/components/atoms/SvgIcon/BetsIcon.js +11 -0
  14. package/dist/src/components/atoms/SvgIcon/BetsWarningIcon.js +11 -0
  15. package/dist/src/components/atoms/SvgIcon/CheckmarkIcon.js +6 -0
  16. package/dist/src/components/atoms/SvgIcon/CollapseIcon.js +6 -0
  17. package/dist/src/components/atoms/SvgIcon/ExpandIcon.js +6 -0
  18. package/dist/src/components/atoms/SvgIcon/FantasyIcon.js +15 -0
  19. package/dist/src/components/atoms/SvgIcon/FlameIcon.js +11 -0
  20. package/dist/src/components/atoms/SvgIcon/Icon.js +44 -0
  21. package/dist/src/components/atoms/SvgIcon/KeyPlaysIcon.js +10 -0
  22. package/dist/src/components/atoms/SvgIcon/MobilePhoneIcon.js +11 -0
  23. package/dist/src/components/atoms/SvgIcon/PlayIcon.js +8 -0
  24. package/dist/src/components/atoms/SvgIcon/ShopIcon.js +7 -0
  25. package/dist/src/components/atoms/SvgIcon/StatsIcon.js +12 -0
  26. package/dist/src/components/atoms/SvgIcon/TimesIcon.js +6 -0
  27. package/dist/src/components/atoms/SvgIcon/WarningIcon.js +8 -0
  28. package/dist/src/components/atoms/SvgIcon/index.js +2 -0
  29. package/dist/src/components/core/App/App.js +35 -0
  30. package/dist/src/components/core/App/index.js +2 -0
  31. package/dist/src/components/core/OverlayContainer.js +58 -0
  32. package/dist/src/components/core/PanelManager/PanelManager.js +86 -0
  33. package/dist/src/components/core/PanelManager/index.js +2 -0
  34. package/dist/src/components/core/ScrollableContainer/FocusableItem.js +38 -0
  35. package/dist/src/components/core/ScrollableContainer/ScrollableContainer.js +168 -0
  36. package/dist/src/components/core/ScrollableContainer/index.js +2 -0
  37. package/dist/src/components/molecules/ActionButton/ActionButton.js +10 -0
  38. package/dist/src/components/molecules/ActionButton/index.js +2 -0
  39. package/dist/src/components/molecules/HeadshotImage/HeadshotImage.js +50 -0
  40. package/dist/src/components/molecules/HeadshotImage/index.js +2 -0
  41. package/dist/src/components/molecules/Overlay/Overlay.js +139 -0
  42. package/dist/src/components/molecules/Overlay/index.js +2 -0
  43. package/dist/src/components/molecules/PanelNavButton/PanelNavButton.js +27 -0
  44. package/dist/src/components/molecules/PanelNavButton/index.js +2 -0
  45. package/dist/src/components/molecules/SegmentButton/SegmentButton.js +10 -0
  46. package/dist/src/components/molecules/SegmentButton/index.js +2 -0
  47. package/dist/src/components/molecules/TabLoadError/TabLoadError.js +14 -0
  48. package/dist/src/components/molecules/TabLoadError/index.js +2 -0
  49. package/dist/src/components/molecules/WatchButton/WatchButton.js +15 -0
  50. package/dist/src/components/molecules/WatchButton/index.js +2 -0
  51. package/dist/src/components/organisms/PanelNavigation/PanelNavigation.js +60 -0
  52. package/dist/src/components/organisms/PanelNavigation/index.js +2 -0
  53. package/dist/src/components/organisms/SegmentController/SegmentController.js +76 -0
  54. package/dist/src/components/organisms/SegmentController/index.js +2 -0
  55. package/dist/src/development/MaestroEventDelegate.js +109 -0
  56. package/dist/src/development/TimeSeek.js +218 -0
  57. package/dist/src/development/development.js +33 -0
  58. package/dist/src/external/AxiosNetworkClient.js +110 -0
  59. package/dist/src/external/LocalStorageCacheManager.js +213 -0
  60. package/dist/src/external/ReactRenderer.js +53 -0
  61. package/dist/src/external/mocks/MockExternalNetworkClient.js +187 -0
  62. package/dist/src/external/ports/cacheManager.js +1 -0
  63. package/dist/src/external/ports/networkClient.js +1 -0
  64. package/dist/src/external/ports/renderer.d.ts +1 -2
  65. package/dist/src/external/ports/renderer.js +1 -0
  66. package/dist/src/external/spatial-navigation/__mocks__/withFocusable.d.ts +8 -0
  67. package/dist/src/external/spatial-navigation/__mocks__/withFocusable.js +7 -0
  68. package/dist/src/external/spatial-navigation/index.js +3 -0
  69. package/dist/src/external/spatial-navigation/measureLayout.js +30 -0
  70. package/dist/src/external/spatial-navigation/spatialNavigation.js +876 -0
  71. package/dist/src/external/spatial-navigation/utils.js +6 -0
  72. package/dist/src/external/spatial-navigation/visualDebugger.js +76 -0
  73. package/dist/src/external/spatial-navigation/withFocusable.js +245 -0
  74. package/dist/src/helpers/Observable.js +37 -0
  75. package/dist/src/helpers/strings.js +14 -0
  76. package/dist/src/helpers/url.js +12 -0
  77. package/dist/src/index.d.ts +5 -5
  78. package/dist/src/index.js +165 -0
  79. package/dist/src/interfaces/IMaestroEvent.d.ts +12 -0
  80. package/dist/src/interfaces/IMaestroEvent.js +18 -0
  81. package/dist/src/interfaces/IMaestroEventDelegate.js +1 -0
  82. package/dist/src/interfaces/IMaestroManager.js +1 -0
  83. package/dist/src/interfaces/IUserSettings.js +20 -0
  84. package/dist/src/models/IModel.js +1 -0
  85. package/dist/src/models/IPanel.js +40 -0
  86. package/dist/src/models/ITheme.js +20 -0
  87. package/dist/src/models/IWinningBet.js +1 -0
  88. package/dist/src/modules/bets/interfaces/IBets.js +1 -0
  89. package/dist/src/modules/bets/mocks.js +573 -0
  90. package/dist/src/modules/bets/types/BetsAnalyticsTypes.js +1 -0
  91. package/dist/src/modules/bets/view/BetsView.js +143 -0
  92. package/dist/src/modules/bets/view/HotProps/HotPropsView.js +150 -0
  93. package/dist/src/modules/bets/view/SixPack/SixPackView.js +110 -0
  94. package/dist/src/modules/bets/view/UserBets/BetResult/BetResult.js +6 -0
  95. package/dist/src/modules/bets/view/UserBets/BetsCard/BetsCard.js +149 -0
  96. package/dist/src/modules/bets/view/UserBets/BetsCard/BetsCardEvent.js +34 -0
  97. package/dist/src/modules/bets/view/UserBets/BetsCard/BetsCardFooter.js +18 -0
  98. package/dist/src/modules/bets/view/UserBets/Boost/Boost.js +6 -0
  99. package/dist/src/modules/bets/view/UserBets/OutcomeBadge/OutcomeBadge.js +9 -0
  100. package/dist/src/modules/bets/view/UserBets/Parlay/Parlay.js +13 -0
  101. package/dist/src/modules/bets/view/UserBets/Straight/Straight.js +6 -0
  102. package/dist/src/modules/bets/view/UserBets/Teaser/Teaser.js +13 -0
  103. package/dist/src/modules/bets/view/UserBets/UserBetsView.js +107 -0
  104. package/dist/src/modules/bets/view/components/Bet/Bet.js +32 -0
  105. package/dist/src/modules/bets/view/components/BetPill/BetPill.js +6 -0
  106. package/dist/src/modules/bets/view/components/BetsCard/BetsCard.js +15 -0
  107. package/dist/src/modules/bets/view/components/BetsCard/BetsCardHeader.js +20 -0
  108. package/dist/src/modules/bets/view/components/GamblingDisclaimer/GamblingDisclaimer.js +14 -0
  109. package/dist/src/modules/bets/view/components/LocationWarning/LocationWarning.js +16 -0
  110. package/dist/src/modules/bets/view/components/PromoCodeBanner/PromoCodeBanner.js +78 -0
  111. package/dist/src/modules/bets/view/components/PromoCodeBanner/index.js +1 -0
  112. package/dist/src/modules/bets/view-model/BetsViewModel.js +974 -0
  113. package/dist/src/modules/fantasy/components/ExpandedAthleteList/ExpandedAthletesList.js +107 -0
  114. package/dist/src/modules/fantasy/components/ExpandedAthleteList/index.js +1 -0
  115. package/dist/src/modules/fantasy/components/LeagueCard/LeagueCard.js +20 -0
  116. package/dist/src/modules/fantasy/components/LeagueCard/index.js +2 -0
  117. package/dist/src/modules/fantasy/components/TopPlayerCard/TopPlayerCard.js +29 -0
  118. package/dist/src/modules/fantasy/components/TopPlayerCard/index.js +2 -0
  119. package/dist/src/modules/fantasy/mocks.js +3682 -0
  120. package/dist/src/modules/fantasy/types.js +6 -0
  121. package/dist/src/modules/fantasy/view/ActiveAthletesMarquee/ActiveAthletesMarquee.js +74 -0
  122. package/dist/src/modules/fantasy/view/ActiveAthletesMarquee/index.js +2 -0
  123. package/dist/src/modules/fantasy/view/ErrorBoundary.d.ts +13 -0
  124. package/dist/src/modules/fantasy/view/FantasyItemView.js +74 -0
  125. package/dist/src/modules/fantasy/view/FantasyView.js +133 -0
  126. package/dist/src/modules/fantasy/view/InactiveLeague/DraftDateCountdown.js +95 -0
  127. package/dist/src/modules/fantasy/view/InactiveLeague/InactiveLeague.js +13 -0
  128. package/dist/src/modules/fantasy/view/InactiveLeague/index.js +2 -0
  129. package/dist/src/modules/fantasy/view/LoginView/LoginView.js +27 -0
  130. package/dist/src/modules/fantasy/view/LoginView/index.js +2 -0
  131. package/dist/src/modules/fantasy/view/MatchupPair/DoubleAthleteCard/DoubleAthleteCard.js +25 -0
  132. package/dist/src/modules/fantasy/view/MatchupPair/DoubleAthleteCard/index.js +2 -0
  133. package/dist/src/modules/fantasy/view/MatchupPair/ExpandedDoubleAthleteCards.js +9 -0
  134. package/dist/src/modules/fantasy/view/MatchupPair/MatchupPair.js +118 -0
  135. package/dist/src/modules/fantasy/view/MatchupPair/index.js +2 -0
  136. package/dist/src/modules/fantasy/view/MatchupSingle/ExpandedSingleAthleteCards.js +9 -0
  137. package/dist/src/modules/fantasy/view/MatchupSingle/MatchupSingle.js +108 -0
  138. package/dist/src/modules/fantasy/view/MatchupSingle/SingleAthleteCard/SingleAthleteCard.js +18 -0
  139. package/dist/src/modules/fantasy/view/MatchupSingle/SingleAthleteCard/index.js +2 -0
  140. package/dist/src/modules/fantasy/view/MatchupSingle/index.js +2 -0
  141. package/dist/src/modules/fantasy/view/PromoView/PromoView.js +12 -0
  142. package/dist/src/modules/fantasy/view/PromoView/index.js +2 -0
  143. package/dist/src/modules/fantasy/view/index.js +2 -0
  144. package/dist/src/modules/fantasy/view-model/FantasyViewModel.FantasyPolling.js +132 -0
  145. package/dist/src/modules/fantasy/view-model/FantasyViewModel.LeagueManagerPolling.js +141 -0
  146. package/dist/src/modules/fantasy/view-model/FantasyViewModel.js +534 -0
  147. package/dist/src/modules/key-plays/constants.js +292 -0
  148. package/dist/src/modules/key-plays/interfaces/IKeyPlays.js +14 -0
  149. package/dist/src/modules/key-plays/interfaces/IKeyPlaysResponse.js +1 -0
  150. package/dist/src/modules/key-plays/view/KeyPlayCardScoreTagView.js +16 -0
  151. package/dist/src/modules/key-plays/view/KeyPlayCardView.js +144 -0
  152. package/dist/src/modules/key-plays/view/KeyPlayErrorStateView.js +65 -0
  153. package/dist/src/modules/key-plays/view/KeyPlaysView.js +151 -0
  154. package/dist/src/modules/key-plays/view-model/KeyPlaysViewModel.js +246 -0
  155. package/dist/src/modules/shop/mocks.js +390 -0
  156. package/dist/src/modules/shop/types.js +7 -0
  157. package/dist/src/modules/shop/view/ProductDetailView/ProductDetailView.js +56 -0
  158. package/dist/src/modules/shop/view/ProductDetailView/index.js +2 -0
  159. package/dist/src/modules/shop/view/ShopView.js +182 -0
  160. package/dist/src/modules/shop/view/components/BrowseCard/BrowseCard.js +23 -0
  161. package/dist/src/modules/shop/view/components/BrowseCard/index.js +2 -0
  162. package/dist/src/modules/shop/view/components/ProductSummaryCard/ProductSummaryCard.js +26 -0
  163. package/dist/src/modules/shop/view/components/ProductSummaryCard/index.js +1 -0
  164. package/dist/src/modules/shop/view/components/ShopPromoView/ShopPromoView.js +19 -0
  165. package/dist/src/modules/shop/view/components/ShopPromoView/index.js +1 -0
  166. package/dist/src/modules/shop/view/components/ShopSection/ShopSection.d.ts +1 -0
  167. package/dist/src/modules/shop/view/components/ShopSection/ShopSection.js +61 -0
  168. package/dist/src/modules/shop/view/components/ShopSection/ShopSectionItem.d.ts +18 -0
  169. package/dist/src/modules/shop/view/components/ShopSection/ShopSectionItem.js +22 -0
  170. package/dist/src/modules/shop/view/components/ShopSection/index.js +1 -0
  171. package/dist/src/modules/shop/view/index.js +2 -0
  172. package/dist/src/modules/shop/view-model/ShopViewModel.ShopPolling.js +111 -0
  173. package/dist/src/modules/shop/view-model/ShopViewModel.js +353 -0
  174. package/dist/src/modules/stats/interfaces/IStats.js +1 -0
  175. package/dist/src/modules/stats/mocks.js +746 -0
  176. package/dist/src/modules/stats/utils/driveSummaryUtils.js +48 -0
  177. package/dist/src/modules/stats/utils/renderTeamStatistic.js +16 -0
  178. package/dist/src/modules/stats/utils.js +62 -0
  179. package/dist/src/modules/stats/view/ActiveAthletes/ActiveAthletes.js +98 -0
  180. package/dist/src/modules/stats/view/ActiveAthletes/CollapsedAthletes/CollapsedAthletes.js +27 -0
  181. package/dist/src/modules/stats/view/ActiveAthletes/ExpandedAthletes/ExpandedAthletes.js +89 -0
  182. package/dist/src/modules/stats/view/ActiveAthletes/index.js +2 -0
  183. package/dist/src/modules/stats/view/DriveSummary/DrivePlayCard.js +70 -0
  184. package/dist/src/modules/stats/view/DriveSummary/DriveSummary.js +87 -0
  185. package/dist/src/modules/stats/view/DriveSummary/ExpandedItems/DriveSummaryExpanded.js +129 -0
  186. package/dist/src/modules/stats/view/DriveSummary/Header/DriveSummaryHeader.js +26 -0
  187. package/dist/src/modules/stats/view/DriveSummary/MainStat/MainStat.js +24 -0
  188. package/dist/src/modules/stats/view/DriveSummary/index.js +2 -0
  189. package/dist/src/modules/stats/view/GameLeaders/GameLeaders.js +48 -0
  190. package/dist/src/modules/stats/view/GameLeaders/index.js +2 -0
  191. package/dist/src/modules/stats/view/MatchPredictor/DonutChart/DonutChart.js +55 -0
  192. package/dist/src/modules/stats/view/MatchPredictor/DonutChart/index.js +1 -0
  193. package/dist/src/modules/stats/view/MatchPredictor/MatchPredictorView.js +60 -0
  194. package/dist/src/modules/stats/view/MatchPredictor/index.js +2 -0
  195. package/dist/src/modules/stats/view/Penalties/Penalties.js +6 -0
  196. package/dist/src/modules/stats/view/Penalties/index.js +2 -0
  197. package/dist/src/modules/stats/view/RecentGames/RecentGames.js +23 -0
  198. package/dist/src/modules/stats/view/RecentGames/index.js +2 -0
  199. package/dist/src/modules/stats/view/ScoringSummary/ExpandedScores/ExpandedScores.js +121 -0
  200. package/dist/src/modules/stats/view/ScoringSummary/ExpandedScores/index.js +2 -0
  201. package/dist/src/modules/stats/view/ScoringSummary/ScoreItem/ScoreItem.js +62 -0
  202. package/dist/src/modules/stats/view/ScoringSummary/ScoreItem/index.js +2 -0
  203. package/dist/src/modules/stats/view/ScoringSummary/ScoresSection/ScoresSection.js +23 -0
  204. package/dist/src/modules/stats/view/ScoringSummary/ScoresSection/index.js +2 -0
  205. package/dist/src/modules/stats/view/ScoringSummary/ScoringSummary.js +106 -0
  206. package/dist/src/modules/stats/view/ScoringSummary/index.js +2 -0
  207. package/dist/src/modules/stats/view/StatsItemView.js +92 -0
  208. package/dist/src/modules/stats/view/StatsQRCode/StatsQRCodeView.js +36 -0
  209. package/dist/src/modules/stats/view/StatsQRCode/index.js +1 -0
  210. package/dist/src/modules/stats/view/StatsView.js +137 -0
  211. package/dist/src/modules/stats/view/TeamStats/TeamStatsView.js +43 -0
  212. package/dist/src/modules/stats/view/TeamStats/index.js +1 -0
  213. package/dist/src/modules/stats/view/Teams/StatsCarousel.js +83 -0
  214. package/dist/src/modules/stats/view/Teams/StatsProgressBar.js +19 -0
  215. package/dist/src/modules/stats/view/Teams/Teams.js +27 -0
  216. package/dist/src/modules/stats/view/Teams/index.js +2 -0
  217. package/dist/src/modules/stats/view/WinProbability/WinProbability.js +28 -0
  218. package/dist/src/modules/stats/view/WinProbability/index.js +2 -0
  219. package/dist/src/modules/stats/view/components/AthletesStatistics/AthletesStatistics.js +89 -0
  220. package/dist/src/modules/stats/view/components/AthletesStatistics/StatisticTable.js +12 -0
  221. package/dist/src/modules/stats/view/components/AthletesStatistics/index.js +2 -0
  222. package/dist/src/modules/stats/view/index.js +2 -0
  223. package/dist/src/modules/stats/view-model/StatsViewModel.d.ts +5 -0
  224. package/dist/src/modules/stats/view-model/StatsViewModel.js +989 -0
  225. package/dist/src/sentry.js +31 -0
  226. package/dist/src/services/AnalyticsService/AnalyticsService.d.ts +12 -7
  227. package/dist/src/services/AnalyticsService/AnalyticsService.js +402 -0
  228. package/dist/src/services/AnalyticsService/types.js +1 -0
  229. package/dist/src/services/BetsService.js +122 -0
  230. package/dist/src/services/FantasyService.js +92 -0
  231. package/dist/src/services/GlobalService/GlobalService.d.ts +9 -0
  232. package/dist/src/services/GlobalService/PageService.d.ts +9 -0
  233. package/dist/src/services/GlobalService/index.d.ts +2 -0
  234. package/dist/src/services/GlobalService/mocks.d.ts +2 -0
  235. package/dist/src/services/GlobalService/types.d.ts +11 -0
  236. package/dist/src/services/InsightsService/InsightsPayloadBuilder.d.ts +1 -2
  237. package/dist/src/services/InsightsService/InsightsPayloadBuilder.js +386 -0
  238. package/dist/src/services/InsightsService/InsightsService.js +74 -0
  239. package/dist/src/services/NetworkManager/NetworkManager.js +457 -0
  240. package/dist/src/services/NetworkManager/NetworkManagerFactory.js +101 -0
  241. package/dist/src/services/NetworkManager/errors.js +45 -0
  242. package/dist/src/services/NetworkManager/types.js +10 -0
  243. package/dist/src/services/PageService/PageService.d.ts +9 -0
  244. package/dist/src/services/PageService/PageService.js +39 -0
  245. package/dist/src/services/PageService/index.d.ts +2 -0
  246. package/dist/src/services/PageService/index.js +2 -0
  247. package/dist/src/services/PageService/mocks.d.ts +2 -0
  248. package/dist/src/services/PageService/mocks.js +104 -0
  249. package/dist/src/services/PageService/types.d.ts +76 -0
  250. package/dist/src/services/PageService/types.js +1 -0
  251. package/dist/src/services/ShopService.js +46 -0
  252. package/dist/src/services/StaticService/PageService.d.ts +9 -0
  253. package/dist/src/services/StaticService/StaticService.d.ts +3 -1
  254. package/dist/src/services/StaticService/StaticService.js +59 -0
  255. package/dist/src/services/StaticService/index.js +2 -0
  256. package/dist/src/services/StaticService/mocks.js +104 -0
  257. package/dist/src/services/StaticService/types.js +1 -0
  258. package/dist/src/services/StatsService.d.ts +1 -0
  259. package/dist/src/services/StatsService.js +147 -0
  260. package/dist/src/services/ThemeManager/constants.js +47 -0
  261. package/dist/src/services/UserSettingsManager.js +155 -0
  262. package/dist/src/test-polyfills.js +15 -0
  263. package/dist/src/types/OverlayTypes.js +4 -0
  264. package/dist/src/view-models/MaestroEventViewModel.d.ts +1 -2
  265. package/dist/src/view-models/MaestroEventViewModel.js +307 -0
  266. package/dist/src/view-models/OverlayViewModel.js +222 -0
  267. package/dist/src/view-models/PanelManagerViewModel.js +212 -0
  268. package/dist/src/view-models/ViewModel.js +32 -0
  269. package/package.json +1 -1
@@ -0,0 +1,974 @@
1
+ import { Observable } from '../../../helpers/Observable';
2
+ import BetsService from '../../../services/BetsService';
3
+ import { UserSettingsManager } from '../../../services/UserSettingsManager';
4
+ import AnalyticsService from '../../../services/AnalyticsService/AnalyticsService';
5
+ // Import ViewModel with correct path
6
+ import { ViewModel } from '../../../view-models/ViewModel';
7
+ import { MOCK_BETS_API_RESPONSE } from '../mocks';
8
+ import SDK from '@/index';
9
+ import { captureException } from '@/sentry';
10
+ import { stringHashCode } from '@/helpers/strings';
11
+ // The interval for polling bet data (in milliseconds)
12
+ const DEFAULT_POLLING_INTERVAL = 30000; // 30 seconds
13
+ const BET_ITEM_TYPE = {
14
+ ODDS: 'odds',
15
+ USER_BETS: 'userBets',
16
+ PROMO: 'promo',
17
+ HOT_PROPS: 'hotProps',
18
+ LOCATION_DISCLAIMER: 'locationDisclaimer',
19
+ GAMBLING_MESSAGE: 'gamblingMessage',
20
+ };
21
+ const BET_ERROR_STATE = {
22
+ NONE: 'none',
23
+ TAB_LOAD_FAILURE: 'tabLoadFailure',
24
+ };
25
+ const isPersonalizedBetsResponse = (data) => {
26
+ return (typeof data === 'object' &&
27
+ data !== null &&
28
+ 'event' in data &&
29
+ typeof data.event === 'object');
30
+ };
31
+ export class BetsViewModel extends ViewModel {
32
+ constructor(eventId, delegate, options = {}) {
33
+ super();
34
+ // Observable properties
35
+ Object.defineProperty(this, "betsItems", {
36
+ enumerable: true,
37
+ configurable: true,
38
+ writable: true,
39
+ value: new Observable([])
40
+ });
41
+ Object.defineProperty(this, "isDataLoading", {
42
+ enumerable: true,
43
+ configurable: true,
44
+ writable: true,
45
+ value: new Observable(true)
46
+ });
47
+ Object.defineProperty(this, "errorState", {
48
+ enumerable: true,
49
+ configurable: true,
50
+ writable: true,
51
+ value: new Observable('none')
52
+ });
53
+ Object.defineProperty(this, "hasEverSuccessfullyLoaded", {
54
+ enumerable: true,
55
+ configurable: true,
56
+ writable: true,
57
+ value: new Observable(false)
58
+ });
59
+ Object.defineProperty(this, "hideWagers", {
60
+ enumerable: true,
61
+ configurable: true,
62
+ writable: true,
63
+ value: new Observable(false)
64
+ });
65
+ // Properties
66
+ Object.defineProperty(this, "eventId", {
67
+ enumerable: true,
68
+ configurable: true,
69
+ writable: true,
70
+ value: void 0
71
+ });
72
+ Object.defineProperty(this, "delegate", {
73
+ enumerable: true,
74
+ configurable: true,
75
+ writable: true,
76
+ value: void 0
77
+ });
78
+ Object.defineProperty(this, "betsService", {
79
+ enumerable: true,
80
+ configurable: true,
81
+ writable: true,
82
+ value: void 0
83
+ });
84
+ Object.defineProperty(this, "analyticsService", {
85
+ enumerable: true,
86
+ configurable: true,
87
+ writable: true,
88
+ value: void 0
89
+ });
90
+ Object.defineProperty(this, "pollingInterval", {
91
+ enumerable: true,
92
+ configurable: true,
93
+ writable: true,
94
+ value: void 0
95
+ });
96
+ Object.defineProperty(this, "pollingTask", {
97
+ enumerable: true,
98
+ configurable: true,
99
+ writable: true,
100
+ value: null
101
+ });
102
+ Object.defineProperty(this, "lastNonPersonalizedResponseHash", {
103
+ enumerable: true,
104
+ configurable: true,
105
+ writable: true,
106
+ value: null
107
+ });
108
+ Object.defineProperty(this, "lastPersonalizedResponseHash", {
109
+ enumerable: true,
110
+ configurable: true,
111
+ writable: true,
112
+ value: null
113
+ });
114
+ Object.defineProperty(this, "useMockData", {
115
+ enumerable: true,
116
+ configurable: true,
117
+ writable: true,
118
+ value: false
119
+ });
120
+ Object.defineProperty(this, "userSettingsManager", {
121
+ enumerable: true,
122
+ configurable: true,
123
+ writable: true,
124
+ value: void 0
125
+ });
126
+ Object.defineProperty(this, "settingsSubscription", {
127
+ enumerable: true,
128
+ configurable: true,
129
+ writable: true,
130
+ value: null
131
+ });
132
+ Object.defineProperty(this, "initialized", {
133
+ enumerable: true,
134
+ configurable: true,
135
+ writable: true,
136
+ value: false
137
+ });
138
+ this.eventId = eventId;
139
+ this.delegate = delegate;
140
+ this.betsService = new BetsService(delegate);
141
+ this.analyticsService = new AnalyticsService();
142
+ this.pollingInterval = DEFAULT_POLLING_INTERVAL;
143
+ this.useMockData = options.useMockData || false;
144
+ this.userSettingsManager = UserSettingsManager.getInstance();
145
+ // Initialize hide_wagers setting
146
+ this.hideWagers.value = this.userSettingsManager.shouldHideWagers();
147
+ // Subscribe to settings changes
148
+ this.settingsSubscription = this.userSettingsManager.settings.subscribe(() => {
149
+ this.updateHideWagersFromSettings();
150
+ });
151
+ this.betsService = new BetsService(delegate);
152
+ }
153
+ /**
154
+ * Initialize the ViewModel and start data fetching
155
+ */
156
+ async init() {
157
+ // Track panel view on initialization
158
+ this.trackPanelView();
159
+ await this.fetchNewData();
160
+ this.refreshPolling();
161
+ this.initialized = true;
162
+ }
163
+ /**
164
+ * Clean up resources when the ViewModel is no longer needed
165
+ */
166
+ cleanup() {
167
+ this.stopPolling();
168
+ // Clean up settings subscription
169
+ if (this.settingsSubscription) {
170
+ this.settingsSubscription();
171
+ this.settingsSubscription = null;
172
+ }
173
+ this.initialized = false;
174
+ }
175
+ /**
176
+ * Track panel view event
177
+ */
178
+ async trackPanelView() {
179
+ try {
180
+ const betCounts = this.getUserBetCounts();
181
+ await this.analyticsService.panelView('espnBet', betCounts);
182
+ }
183
+ catch (error) {
184
+ captureException(error, {
185
+ context: 'BetsViewModel.trackPanelView',
186
+ });
187
+ console.error('[BET] Error tracking panel view:', error);
188
+ }
189
+ }
190
+ /**
191
+ * Track card click event
192
+ */
193
+ async trackCardClick(cardType, betType) {
194
+ try {
195
+ const betCounts = this.getUserBetCounts();
196
+ const metadata = {
197
+ ...betCounts,
198
+ card: cardType,
199
+ bet_type: betType || null,
200
+ };
201
+ await this.analyticsService.panelClick({
202
+ type: 'espnBet',
203
+ name: 'bets_item_clicked',
204
+ metadata,
205
+ });
206
+ }
207
+ catch (error) {
208
+ captureException(error, {
209
+ context: 'BetsViewModel.trackCardClick',
210
+ cardType,
211
+ betType,
212
+ });
213
+ console.error('[BET] Error tracking card click:', error);
214
+ }
215
+ }
216
+ /**
217
+ * Get user bet counts for analytics
218
+ */
219
+ getUserBetCounts() {
220
+ try {
221
+ let inGameBets = 0;
222
+ let totalBets = 0;
223
+ // Count bets from current data
224
+ for (const item of this.betsItems.value) {
225
+ if (item.type === 'userBets') {
226
+ const userBetsItem = item;
227
+ const allBets = [
228
+ ...userBetsItem.data.bets.live,
229
+ ...userBetsItem.data.bets.settled,
230
+ ...userBetsItem.data.bets.upcoming,
231
+ ];
232
+ totalBets = allBets.length;
233
+ inGameBets = userBetsItem.data.bets.live.length;
234
+ break;
235
+ }
236
+ }
237
+ return {
238
+ in_game_bets: inGameBets,
239
+ total_bets: totalBets,
240
+ };
241
+ }
242
+ catch (error) {
243
+ captureException(error, {
244
+ context: 'BetsViewModel.getUserBetCounts',
245
+ });
246
+ console.error('[BET] Error getting user bet counts:', error);
247
+ return {
248
+ in_game_bets: 0,
249
+ total_bets: 0,
250
+ };
251
+ }
252
+ }
253
+ /**
254
+ * Refresh polling for bet data
255
+ */
256
+ refreshPolling() {
257
+ // First stop any existing polling
258
+ this.stopPolling();
259
+ if (this.useMockData) {
260
+ return;
261
+ }
262
+ // We'll determine whether to use authenticated polling in the polling task
263
+ this.pollingTask = {
264
+ stop: () => {
265
+ // This will be replaced when actual polling starts
266
+ },
267
+ };
268
+ // Start the poll loop
269
+ this.pollWithInterval();
270
+ }
271
+ /**
272
+ * Get user credentials from delegate
273
+ */
274
+ async getUserCredentials() {
275
+ const swid = (await this.delegate.getUserSWID()) || '';
276
+ const jwt = (await this.delegate.getUserToken()) || '';
277
+ return { swid, jwt };
278
+ }
279
+ /**
280
+ * @description Update polling interval checking either response header or panel config.
281
+ * Refresh polling if necessary.
282
+ */
283
+ updatePollingInterval(headers) {
284
+ const headerInterval = this.getResponseHeaderPollingIntervalValue(headers);
285
+ const panelConfigInterval = this.getPanelConfigInterval();
286
+ const newInterval = headerInterval || panelConfigInterval; // order matters here
287
+ if (newInterval && newInterval !== this.pollingInterval) {
288
+ this.pollingInterval = newInterval;
289
+ console.log(`[BET] Updating polling interval to ${this.pollingInterval} milliseconds`);
290
+ // If we have an active polling task, restart it with the new interval
291
+ this.refreshPolling();
292
+ }
293
+ }
294
+ getPanelConfigInterval() {
295
+ const config = SDK.getPageConfig();
296
+ const panel = config?.panels?.find((p) => p.panel_type === 'espnBet');
297
+ if (!panel) {
298
+ return null;
299
+ }
300
+ const oddsPollingInterval = panel.renderer?.block_data?.odds_polling_interval;
301
+ if (!oddsPollingInterval) {
302
+ return null;
303
+ }
304
+ const parsedInterval = parseInt(oddsPollingInterval, 10);
305
+ if (isNaN(parsedInterval) || parsedInterval <= 0) {
306
+ console.warn(`[BET] Invalid polling interval from panel config: ${oddsPollingInterval}.`);
307
+ return null;
308
+ }
309
+ return parsedInterval * 1000; // Convert seconds to milliseconds
310
+ }
311
+ getResponseHeaderPollingIntervalValue(headers) {
312
+ const newInterval = headers['polling-interval'];
313
+ if (!newInterval) {
314
+ return null;
315
+ }
316
+ const parsedInterval = parseInt(newInterval, 10);
317
+ if (isNaN(parsedInterval) || parsedInterval <= 0) {
318
+ console.warn(`[BET] Invalid polling interval header received: ${newInterval}.`);
319
+ return null;
320
+ }
321
+ return parsedInterval * 1000; // Convert seconds to milliseconds
322
+ }
323
+ /**
324
+ * Poll with the personalized endpoint for both authenticated and anonymous users
325
+ *
326
+ * SWID format is provided by the client app delegate:
327
+ * - Logged in: {6D201B58-440C-47A6-970B-0F8C78C0BAB8} (with curly braces)
328
+ * - Anonymous: 6D201B58-440C-47A6-970B-0F8C78C0BAB8 (without curly braces)
329
+ * - Failsafe: Empty string if local storage fails
330
+ */
331
+ async pollWithInterval() {
332
+ try {
333
+ // Always use personalized endpoint
334
+ const { swid, jwt } = await this.getUserCredentials();
335
+ // Start personalized polling for both authenticated and anonymous users with headers
336
+ this.pollingTask = this.betsService.pollPersonalizedBetsWithHeaders(this.eventId, swid, jwt, this.pollingInterval, (response) => {
337
+ this.updatePollingInterval(response.headers);
338
+ this.processPolledData(response.data);
339
+ }, this.handlePollingError.bind(this));
340
+ }
341
+ catch (error) {
342
+ captureException(error, {
343
+ context: 'BetsViewModel.pollWithInterval',
344
+ });
345
+ console.error('[BET] Error setting up polling:', error);
346
+ }
347
+ }
348
+ /**
349
+ * Process data received from polling
350
+ */
351
+ processPolledData(data) {
352
+ // Calculate hash for the response to check if it's different from the last one
353
+ const responseHash = stringHashCode(JSON.stringify(data));
354
+ const isPersonalized = isPersonalizedBetsResponse(data);
355
+ if (isPersonalized) {
356
+ // Check if this is the same as the last successful response
357
+ if (this.lastPersonalizedResponseHash === responseHash &&
358
+ this.hasEverSuccessfullyLoaded.value) {
359
+ console.log('[BET] Response hash is the same as last successful response - skipping update');
360
+ return;
361
+ }
362
+ // Update tracking variables
363
+ this.lastPersonalizedResponseHash = responseHash;
364
+ // this.lastSuccessfulResponsePersonalized =
365
+ // data as IPersonalizedBetsResponse;
366
+ }
367
+ else {
368
+ // Check if this is the same as the last successful response
369
+ if (this.lastNonPersonalizedResponseHash === responseHash &&
370
+ this.hasEverSuccessfullyLoaded.value) {
371
+ console.log('[BET] Response hash is the same as last successful response - skipping update');
372
+ return;
373
+ }
374
+ // Update tracking variables
375
+ this.lastNonPersonalizedResponseHash = responseHash;
376
+ // this.lastSuccessfulResponseNonPersonalized = data as IBetsResponse;
377
+ }
378
+ // Update state
379
+ this.hasEverSuccessfullyLoaded.value = true;
380
+ this.errorState.value = 'none';
381
+ // Process the response and update betsItems
382
+ // This is a placeholder - you would need to implement the actual logic
383
+ // for parsing different parts of the response (sixPacks, hot props, etc.)
384
+ this.processBetsData(data);
385
+ }
386
+ /**
387
+ * Process the bets data and update the betsItems
388
+ */
389
+ processBetsData(data) {
390
+ // Clear current items
391
+ const newItems = [];
392
+ if (!Object.keys(data).length) {
393
+ return;
394
+ }
395
+ // 1. Process regular odds (always available in both response types)
396
+ const sixPack = this.processSixPackData(data);
397
+ if (sixPack) {
398
+ newItems.push(sixPack);
399
+ }
400
+ // 2. Process location disclaimer if this is a personalized response
401
+ const locationDisclaimer = this.processLocationDisclaimer(data);
402
+ if (locationDisclaimer) {
403
+ newItems.push(locationDisclaimer);
404
+ }
405
+ // 3. Process user bets if this is a personalized response
406
+ const userBets = this.processUserBetsData(data);
407
+ if (userBets) {
408
+ newItems.push(userBets);
409
+ }
410
+ // 4. Process featured(hot props) bets if available
411
+ const hotProps = this.processHotProps(data);
412
+ if (hotProps) {
413
+ newItems.push(hotProps);
414
+ }
415
+ // 5. Process promo code
416
+ const promoCode = this.processPromoCode(data);
417
+ if (promoCode) {
418
+ newItems.push(promoCode);
419
+ }
420
+ // 6. Process gambling message if available
421
+ const gamblingMessage = this.processGamblingMessage(data);
422
+ if (gamblingMessage) {
423
+ newItems.push(gamblingMessage);
424
+ }
425
+ // Update the observable
426
+ this.betsItems.value = newItems;
427
+ }
428
+ processLocationDisclaimer(data) {
429
+ const isPersonalized = isPersonalizedBetsResponse(data);
430
+ if (!isPersonalized || !data.disclaimers?.location) {
431
+ return null;
432
+ }
433
+ return {
434
+ type: 'locationDisclaimer',
435
+ id: 'locationDisclaimer',
436
+ data: {
437
+ text: data.disclaimers.location,
438
+ },
439
+ };
440
+ }
441
+ processPromoCode(data) {
442
+ const isPersonalized = isPersonalizedBetsResponse(data);
443
+ if (!isPersonalized || !data.promotion) {
444
+ return null;
445
+ }
446
+ return {
447
+ type: 'promo',
448
+ data: {
449
+ title: data.promotion.title || '',
450
+ description: data.promotion.description || '',
451
+ logo: data.promotion.logo || '',
452
+ qrCode: data.promotion.qrCode || '',
453
+ backgroundImage: data.promotion.backgroundImage || '',
454
+ },
455
+ id: `promo-${data.event?.id || data.id}`,
456
+ };
457
+ }
458
+ processGamblingMessage(data) {
459
+ const isPersonalized = isPersonalizedBetsResponse(data);
460
+ if (!isPersonalized || !data.disclaimers?.gambling) {
461
+ return null;
462
+ }
463
+ return {
464
+ type: 'gamblingMessage',
465
+ id: 'gamblingMessage',
466
+ data: {
467
+ text: data.disclaimers.gambling,
468
+ },
469
+ };
470
+ }
471
+ processSixPackData(response) {
472
+ const isPersonalized = isPersonalizedBetsResponse(response);
473
+ const teams = response.entities?.teams;
474
+ const odds = !isPersonalized
475
+ ? response.bets?.odds
476
+ : response.event?.bets?.odds;
477
+ const gameState = !isPersonalized
478
+ ? response.gameState
479
+ : response.event?.gameState;
480
+ const sport = !isPersonalized ? response.sport : response.event?.sport;
481
+ const eventId = !isPersonalized ? response.id : response.event?.id;
482
+ const isSoccer = !isPersonalized
483
+ ? response.sport === 'soccer'
484
+ : response.event?.sport === 'soccer';
485
+ if (!odds || !gameState || !sport || !eventId || !teams) {
486
+ return null;
487
+ }
488
+ const teamArray = Object.values(teams);
489
+ let awayTeam;
490
+ let homeTeam;
491
+ // Find home and away teams from odds
492
+ for (const team of Object.values(teams)) {
493
+ const spreadOdds = odds.find((odd) => odd.type === 'pointSpread');
494
+ if (spreadOdds) {
495
+ if (spreadOdds.values.some((v) => v.team === team.id && v.type === 'home')) {
496
+ homeTeam = team;
497
+ }
498
+ else if (spreadOdds.values.some((v) => v.team === team.id && v.type === 'away')) {
499
+ awayTeam = team;
500
+ }
501
+ }
502
+ }
503
+ // Fallback logic if we couldn't determine from odds
504
+ if (!homeTeam || !awayTeam) {
505
+ console.warn('Could not determine home/away teams from odds, using first two teams');
506
+ switch (teamArray.length) {
507
+ case 0:
508
+ throw new Error('No teams found in response');
509
+ case 1:
510
+ // Only one team - unusual case, but handle gracefully
511
+ homeTeam = teamArray[0];
512
+ awayTeam = teamArray[0]; // Same team as both home and away (shouldn't happen)
513
+ break;
514
+ case 2:
515
+ // Default to first team as home, second as away
516
+ homeTeam = teamArray[0];
517
+ awayTeam = teamArray[1];
518
+ break;
519
+ default:
520
+ // More than 2 teams - take first two and log warning
521
+ homeTeam = teamArray[0];
522
+ awayTeam = teamArray[1];
523
+ console.warn(`Warning: Unexpected team count (${teamArray.length}) in six pack response`);
524
+ }
525
+ }
526
+ if (!awayTeam || !homeTeam) {
527
+ throw new Error('Could not determine home or away team from response');
528
+ }
529
+ const isInProgress = gameState === 'in';
530
+ const isHomeFavoured = (() => {
531
+ if (isSoccer && !isInProgress) {
532
+ return homeTeam.favoredAtOpen ?? true;
533
+ }
534
+ return homeTeam.favored ?? true;
535
+ })();
536
+ const isAwayFavoured = (() => {
537
+ if (isSoccer && !isInProgress) {
538
+ return awayTeam.favoredAtOpen ?? false;
539
+ }
540
+ return awayTeam.favored ?? false;
541
+ })();
542
+ let awayTeamStartGameFirstLine = '';
543
+ let awayTeamStartGameSecondLine = '';
544
+ let homeTeamStartGameFirstLine = '';
545
+ let homeTeamStartGameSecondLine = '';
546
+ let awaySpreadFirstLine = '';
547
+ let awaySpreadSecondLine = '';
548
+ let homeSpreadFirstLine = '';
549
+ let homeSpreadSecondLine = '';
550
+ let awayTotalFirstLine = '';
551
+ let awayTotalSecondLine = '';
552
+ let homeTotalFirstLine = '';
553
+ let homeTotalSecondLine = '';
554
+ let awayMoneylineFirstLine = '';
555
+ let homeMoneylineFirstLine = '';
556
+ let drawGameStateFirstLine = '';
557
+ let drawGameStateSecondLine = '';
558
+ let drawMoneylineFirstLine = '';
559
+ const itemTypeSet = new Set();
560
+ const orderedColumns = [];
561
+ for (const oddsItem of odds) {
562
+ if (!itemTypeSet.has(oddsItem.type)) {
563
+ orderedColumns.push({
564
+ type: oddsItem.type,
565
+ abbreviation: oddsItem.abbreviation,
566
+ });
567
+ itemTypeSet.add(oddsItem.type);
568
+ }
569
+ const isLive = oddsItem.state === 'live';
570
+ const isClose = oddsItem.state === 'close';
571
+ const isOpen = oddsItem.state === 'open';
572
+ // For every odd type and using the correct state based on game state, get the odds and lines for the home and away team.
573
+ // There are special handlings for the 'start of game' lines. It can be from 'spread', 'total' or 'moneyline' depending on which team is favoured, sport, etc.
574
+ switch (oddsItem.type) {
575
+ case 'pointSpread': {
576
+ const spreadCorrectState = isInProgress ? isLive : isClose;
577
+ if (spreadCorrectState) {
578
+ for (const value of oddsItem.values) {
579
+ if (value.type === 'home') {
580
+ homeSpreadFirstLine = value.line ?? '';
581
+ homeSpreadSecondLine = value.odds ?? '';
582
+ }
583
+ if (value.type === 'away') {
584
+ awaySpreadFirstLine = value.line ?? '';
585
+ awaySpreadSecondLine = value.odds ?? '';
586
+ }
587
+ }
588
+ }
589
+ const pointsSpreadStartOfGameCorrectState = isInProgress
590
+ ? isClose
591
+ : isOpen;
592
+ if (pointsSpreadStartOfGameCorrectState && !isSoccer) {
593
+ if (isHomeFavoured) {
594
+ for (const value of oddsItem.values) {
595
+ if (value.type === 'home') {
596
+ homeTeamStartGameFirstLine = value.line ?? '';
597
+ homeTeamStartGameSecondLine = value.odds ?? '';
598
+ }
599
+ }
600
+ }
601
+ if (isAwayFavoured) {
602
+ for (const value of oddsItem.values) {
603
+ if (value.type === 'away') {
604
+ awayTeamStartGameFirstLine = value.line ?? '';
605
+ awayTeamStartGameSecondLine = value.odds ?? '';
606
+ }
607
+ }
608
+ }
609
+ }
610
+ break;
611
+ }
612
+ case 'total': {
613
+ const totalCorrectState = isInProgress ? isLive : isClose;
614
+ if (totalCorrectState) {
615
+ for (const value of oddsItem.values) {
616
+ if (value.type === 'over') {
617
+ if (isSoccer) {
618
+ homeTotalFirstLine = value.line ?? '';
619
+ homeTotalSecondLine = value.odds ?? '';
620
+ }
621
+ else {
622
+ awayTotalFirstLine = value.line ?? '';
623
+ awayTotalSecondLine = value.odds ?? '';
624
+ }
625
+ }
626
+ if (value.type === 'under') {
627
+ if (isSoccer) {
628
+ awayTotalFirstLine = value.line ?? '';
629
+ awayTotalSecondLine = value.odds ?? '';
630
+ }
631
+ else {
632
+ homeTotalFirstLine = value.line ?? '';
633
+ homeTotalSecondLine = value.odds ?? '';
634
+ }
635
+ }
636
+ }
637
+ }
638
+ const totalStartOfGameCorrectState = isInProgress ? isClose : isOpen;
639
+ if (totalStartOfGameCorrectState) {
640
+ // If home is not favoured, use total as the start of the game line
641
+ if (!isHomeFavoured) {
642
+ for (const value of oddsItem.values) {
643
+ if ((isSoccer && value.type === 'over') ||
644
+ (!isSoccer && value.type === 'under')) {
645
+ homeTeamStartGameFirstLine = value.line ?? '';
646
+ homeTeamStartGameSecondLine = value.odds ?? '';
647
+ }
648
+ }
649
+ }
650
+ // If away is not favoured, use total as the start of the game line
651
+ if (!isAwayFavoured) {
652
+ for (const value of oddsItem.values) {
653
+ if ((isSoccer && value.type === 'under') ||
654
+ (!isSoccer && value.type === 'over')) {
655
+ awayTeamStartGameFirstLine = value.line ?? '';
656
+ awayTeamStartGameSecondLine = value.odds ?? '';
657
+ }
658
+ }
659
+ }
660
+ }
661
+ break;
662
+ }
663
+ case 'moneyline': {
664
+ const mlCorrectState = isInProgress ? isLive : isClose;
665
+ if (mlCorrectState) {
666
+ for (const value of oddsItem.values) {
667
+ if (value.type === 'home') {
668
+ homeMoneylineFirstLine = value.odds ?? '';
669
+ }
670
+ if (value.type === 'away') {
671
+ awayMoneylineFirstLine = value.odds ?? '';
672
+ }
673
+ }
674
+ }
675
+ // soccer logic
676
+ if (isSoccer) {
677
+ const moneyLineStartOfGameCorrectState = isInProgress
678
+ ? isClose
679
+ : isOpen;
680
+ // find draw value
681
+ for (const value of oddsItem.values) {
682
+ if (value.type === 'draw') {
683
+ if (moneyLineStartOfGameCorrectState) {
684
+ drawGameStateFirstLine = value.odds ?? '';
685
+ drawGameStateSecondLine = 'DRAW';
686
+ }
687
+ if (mlCorrectState) {
688
+ drawMoneylineFirstLine = value.odds ?? '';
689
+ }
690
+ }
691
+ }
692
+ if (moneyLineStartOfGameCorrectState) {
693
+ if (isHomeFavoured) {
694
+ for (const value of oddsItem.values) {
695
+ if (value.type === 'home') {
696
+ homeTeamStartGameFirstLine = value.odds ?? '';
697
+ homeTeamStartGameSecondLine = oddsItem.abbreviation ?? '';
698
+ }
699
+ }
700
+ }
701
+ if (isAwayFavoured) {
702
+ for (const value of oddsItem.values) {
703
+ if (value.type === 'away') {
704
+ awayTeamStartGameFirstLine = value.odds ?? '';
705
+ awayTeamStartGameSecondLine = oddsItem.abbreviation ?? '';
706
+ }
707
+ }
708
+ }
709
+ }
710
+ }
711
+ break;
712
+ }
713
+ }
714
+ }
715
+ // Create the six pack data structure
716
+ const awayTeamOdds = {
717
+ teamAbbreviation: awayTeam.abbreviation ?? '',
718
+ startOfGameFirstLine: awayTeamStartGameFirstLine,
719
+ startOfGameSecondLine: awayTeamStartGameSecondLine,
720
+ gameSpreadFirstLine: awaySpreadFirstLine,
721
+ gameSpreadSecondLine: awaySpreadSecondLine,
722
+ totalPointsFirstLine: awayTotalFirstLine,
723
+ totalPointsSecondLine: awayTotalSecondLine,
724
+ moneylineFirstLine: awayMoneylineFirstLine,
725
+ };
726
+ const homeTeamOdds = {
727
+ teamAbbreviation: homeTeam.abbreviation ?? '',
728
+ startOfGameFirstLine: homeTeamStartGameFirstLine,
729
+ startOfGameSecondLine: homeTeamStartGameSecondLine,
730
+ gameSpreadFirstLine: homeSpreadFirstLine,
731
+ gameSpreadSecondLine: homeSpreadSecondLine,
732
+ totalPointsFirstLine: homeTotalFirstLine,
733
+ totalPointsSecondLine: homeTotalSecondLine,
734
+ moneylineFirstLine: homeMoneylineFirstLine,
735
+ };
736
+ const drawOdds = {
737
+ teamAbbreviation: 'DRAW',
738
+ startOfGameFirstLine: drawGameStateFirstLine,
739
+ startOfGameSecondLine: drawGameStateSecondLine,
740
+ gameSpreadFirstLine: '',
741
+ gameSpreadSecondLine: '',
742
+ totalPointsFirstLine: '',
743
+ totalPointsSecondLine: '',
744
+ moneylineFirstLine: drawMoneylineFirstLine,
745
+ };
746
+ const sixPack = {
747
+ topTeamOdds: isSoccer ? homeTeamOdds : awayTeamOdds,
748
+ bottomTeamOdds: isSoccer ? awayTeamOdds : homeTeamOdds,
749
+ drawOdds: drawGameStateFirstLine &&
750
+ drawGameStateSecondLine &&
751
+ drawMoneylineFirstLine
752
+ ? drawOdds
753
+ : undefined,
754
+ orderedColumns,
755
+ gameState,
756
+ };
757
+ return {
758
+ id: `sixpack-${eventId}`,
759
+ type: 'sixPack',
760
+ data: sixPack,
761
+ teamInfo: {
762
+ home: homeTeam,
763
+ away: awayTeam,
764
+ },
765
+ hideWagers: this.hideWagers.value,
766
+ };
767
+ }
768
+ processUserBetsData(data) {
769
+ const isPersonalized = isPersonalizedBetsResponse(data);
770
+ if (!isPersonalized || !data.event || !data.betsModule) {
771
+ return null;
772
+ }
773
+ const betItems = {
774
+ live: data.betsModule.live.filter((bet) => bet.type !== 'promo'),
775
+ settled: data.betsModule.settled.filter((bet) => bet.type !== 'promo'),
776
+ upcoming: data.betsModule.upcoming.filter((bet) => bet.type !== 'promo'),
777
+ };
778
+ return {
779
+ data: {
780
+ bets: betItems,
781
+ defaultTab: data.betsModule.defaultTab,
782
+ },
783
+ id: `userBets-${data.event.id}`,
784
+ type: 'userBets',
785
+ hideWagers: this.hideWagers.value,
786
+ };
787
+ }
788
+ processHotProps(response) {
789
+ const isPersonalized = isPersonalizedBetsResponse(response);
790
+ const hotProps = isPersonalized
791
+ ? response.event?.featured
792
+ : response.featured;
793
+ if (!hotProps || !Array.isArray(hotProps)) {
794
+ return null;
795
+ }
796
+ const hasData = hotProps.every((hotProp) => Boolean(hotProp && hotProp.id));
797
+ if (!hasData) {
798
+ return null;
799
+ }
800
+ const filteredHotProps = hotProps.filter((hotProp) => hotProp.selections.length > 0);
801
+ return {
802
+ type: 'hotProps',
803
+ hideWagers: this.hideWagers.value,
804
+ id: 'hotProps',
805
+ data: {
806
+ items: filteredHotProps.map((hotProp) => {
807
+ const bet = hotProp.selections[0];
808
+ return {
809
+ id: bet.id,
810
+ odds: bet.odds.current || '',
811
+ marketText: bet.text,
812
+ selectionText: hotProp.text,
813
+ status: bet.status,
814
+ type: bet.type,
815
+ icon: bet.icon,
816
+ iconDark: bet.iconDark,
817
+ outcome: bet.outcome,
818
+ };
819
+ }),
820
+ },
821
+ };
822
+ }
823
+ /**
824
+ * Handle errors that occur during polling
825
+ */
826
+ handlePollingError(error) {
827
+ console.error('Polling error:', error);
828
+ if (!this.hasEverSuccessfullyLoaded.value) {
829
+ // If we've never had a successful load, show an error state
830
+ this.errorState.value = 'tabLoadFailure';
831
+ this.betsItems.value = [];
832
+ }
833
+ else {
834
+ // If we had a previous successful load, keep showing the old data
835
+ // No need to update errorState or betsItems
836
+ }
837
+ }
838
+ /**
839
+ * Stop polling for bet data
840
+ */
841
+ stopPolling() {
842
+ if (this.pollingTask) {
843
+ this.pollingTask.stop();
844
+ this.pollingTask = null;
845
+ }
846
+ if (this.isDataLoading.value) {
847
+ this.isDataLoading.value = false;
848
+ }
849
+ }
850
+ /**
851
+ * Fetch new bet data immediately (one-time)
852
+ */
853
+ async fetchNewData() {
854
+ this.isDataLoading.value = true;
855
+ try {
856
+ // Handle mock data if enabled
857
+ if (this.useMockData) {
858
+ this.betsItems.value = [];
859
+ await this.fetchMockData();
860
+ return;
861
+ }
862
+ // Always use personalized endpoint for both authenticated and anonymous users
863
+ await this.fetchPersonalizedData();
864
+ this.isDataLoading.value = false;
865
+ }
866
+ catch (error) {
867
+ captureException(error, {
868
+ context: 'BetsViewModel.fetchNewData',
869
+ });
870
+ console.error('[BET] Error fetching bet data:', error);
871
+ if (!this.hasEverSuccessfullyLoaded.value) {
872
+ this.errorState.value = 'tabLoadFailure';
873
+ this.betsItems.value = [];
874
+ }
875
+ this.isDataLoading.value = false;
876
+ }
877
+ }
878
+ /**
879
+ * Fetch personalized bet data
880
+ */
881
+ async fetchPersonalizedData() {
882
+ try {
883
+ const { swid, jwt } = await this.getUserCredentials();
884
+ const response = await this.betsService.getPersonalizedBets(this.eventId, swid, jwt);
885
+ // Process the response
886
+ this.processPolledData(response);
887
+ }
888
+ catch (error) {
889
+ captureException(error, {
890
+ context: 'BetsViewModel.fetchPersonalizedData',
891
+ });
892
+ console.error('[BET] Error fetching personalized bets:', error);
893
+ throw error;
894
+ }
895
+ }
896
+ /**
897
+ * Fetch mock data for testing
898
+ */
899
+ async fetchMockData() {
900
+ this.processBetsData(MOCK_BETS_API_RESPONSE);
901
+ this.hasEverSuccessfullyLoaded.value = true;
902
+ this.errorState.value = 'none';
903
+ this.isDataLoading.value = false;
904
+ }
905
+ simulateUseCase(useCase, _) {
906
+ switch (useCase) {
907
+ case 'betsTabLoadFailure':
908
+ this.errorState.value = 'tabLoadFailure';
909
+ break;
910
+ case 'betsMockData':
911
+ this.stopPolling();
912
+ this.useMockData = true;
913
+ this.lastPersonalizedResponseHash = null;
914
+ this.lastNonPersonalizedResponseHash = null;
915
+ this.errorState.value = 'none';
916
+ this.fetchNewData();
917
+ break;
918
+ case 'betsLiveData':
919
+ this.useMockData = false;
920
+ this.lastPersonalizedResponseHash = null;
921
+ this.lastNonPersonalizedResponseHash = null;
922
+ this.hasEverSuccessfullyLoaded.value = false;
923
+ this.errorState.value = 'none';
924
+ this.fetchNewData();
925
+ this.refreshPolling();
926
+ break;
927
+ default:
928
+ throw new Error(`Unsupported use case: ${useCase}`);
929
+ }
930
+ }
931
+ clearTabLoadFailure() {
932
+ this.errorState.value = 'none';
933
+ setTimeout(() => {
934
+ this.refreshPolling();
935
+ }, 100);
936
+ }
937
+ /**
938
+ * Check if wagers should be hidden based on user settings
939
+ */
940
+ shouldHideWagers() {
941
+ return this.hideWagers.value;
942
+ }
943
+ /**
944
+ * Update hideWagers Observable from current settings
945
+ */
946
+ updateHideWagersFromSettings() {
947
+ this.hideWagers.value = this.userSettingsManager.shouldHideWagers();
948
+ this.refreshBetItemsWithNewSettings();
949
+ }
950
+ refreshBetItemsWithNewSettings() {
951
+ this.betsItems.value = this.betsItems.value.map((item) => {
952
+ if (item.type === 'sixPack') {
953
+ return {
954
+ ...item,
955
+ hideWagers: this.hideWagers.value,
956
+ };
957
+ }
958
+ if (item.type === 'userBets') {
959
+ return {
960
+ ...item,
961
+ hideWagers: this.hideWagers.value,
962
+ };
963
+ }
964
+ if (item.type === 'hotProps') {
965
+ return {
966
+ ...item,
967
+ hideWagers: this.hideWagers.value,
968
+ };
969
+ }
970
+ return item;
971
+ });
972
+ }
973
+ }
974
+ export default BetsViewModel;