import { useRouter } from 'vue-router' import { getQueryParamObjectFromPath, removeQueryParam } from '#lib/utils' import { IFRAME_GAME_API_MAP } from '#lib/constants' import type { GameOptions, PlayGameApiReponse } from '#lib/types' import { GameProviderEnum, CasinoProviderEnum, IFrameGameURLEnum, PlayGameSchemeEnum, } from '#lib/enums' import { useGameToast, useCheckGame, useGameURL, useNadal, usePlayGameInWebview, } from '#lib/composables' import { useRoute, useRuntimeConfig } from 'nuxt/app' import { useDevice } from '#imports' import { GAME_API } from '#lib/configs/api' import { storeToRefs } from 'pinia' import { useGamesStore, useModalsStore } from '#lib/stores' /** * Provides functions for handling game-related actions including playing games, * constructing game URLs, and managing game-related state. * This composable integrates various services and utilities to manage game logic * and user interactions related to games. * * @returns {Object} An object containing functions for playing games, getting game URLs, and handling game-related actions. * @namespace */ export function useGames() { const { game } = useNadal() const { openModalMaintainAthenaGames, onShowMaintainGame, onShowCommingSoon, onShowPromotionPrevent, onShowNotEnoughBalance, openMaintainProvider, } = useGameToast() const { checkCommingSoon, checkMaintainProvider, checkMaintainAthenaGames, checkLogin, checkPromotion, checkBalance, } = useCheckGame() const gamesStore = useGamesStore() const { lastGame } = storeToRefs(gamesStore) const { getGameUrlIframe, getGameLiveStreamUrl } = useGameURL() const route = useRoute() const router = useRouter() const { isMobile, isSafari } = useDevice() const { setOpenAuthModal } = useModalsStore() const { isPlayingInWebview } = usePlayGameInWebview() /** * Checks and plays the game based on its type, URL, and provider. * Opens the game in an iframe or a new tab depending on the game configuration. * * @param {GameOptions} game - The game options including gameUrl, gameId, provider, etc. * @returns {boolean} True if the game was successfully handled, otherwise false. */ const checkAndPlayIFameGame = (game: GameOptions) => { const { gameUrl, provider, isCasino, newTab, ID, gameId = '', apiUrl = '', } = game const isNewTab = newTab || isMobile if ( provider === GameProviderEnum.Sunwin || provider === GameProviderEnum['789club'] ) { openGameAbsoluteUrl(getGameLiveStreamUrl(apiUrl, gameId), '', true) return true } const urlOpenIframe = getGameUrlIframe(gameUrl!) if (urlOpenIframe) { openGameAbsoluteUrl(urlOpenIframe, gameUrl!, isNewTab) return true } if (gameUrl) { openGameRelativeUrl( gameUrl.indexOf('?') > 0 ? `${gameUrl}&provider=${provider}&gameId=${ID}` : `${gameUrl}?provider=${provider}&gameId=${ID}`, isNewTab, provider!, isCasino, ) return true } return false } /** * Constructs the API path for the game based on its options and provider. * * @param {GameOptions} game - The game options including apiUrl, gameId, provider, etc. * @returns {string} The constructed API path for the game. */ const getAPIPath = (game: GameOptions): string => { const { apiUrl = '', ID = undefined, gameId = '', provider = '', gameType, } = game const homeUrl = getHomeURL() const returnUrl: string = encodeURIComponent(window.location.href) if (apiUrl) { return gameId ? `${apiUrl}?gameName=${gameId}` : apiUrl } if ( Object.values(GameProviderEnum).includes(provider as GameProviderEnum) ) { const params = new URLSearchParams({ provider, id: ID?.toString() ?? '', gameId: gameId!, gameType: gameType ?? '', isMobile: isMobile.toString(), returnUrl, homeUrl, }) return `${GAME_API.PLAY_GAME}?${params.toString()}` } const params = new URLSearchParams({ provider, homeUrl, lobbyUrl: homeUrl, }) const gamePath = getGamePath(GAME_API.PLAY_GAME, gameId, provider, ID) return `${gamePath}&${params.toString()}` } /** * Constructs the URL for the game tab based on its options and provider. * * @param {GameOptions} game - The game options including provider, withoutIframe, urlLocation, etc. * @returns {Promise} The constructed tab URL. */ const getTabURL = async (game: GameOptions) => { const { provider = '', withoutIframe, urlLocation = '' } = game const apiPath = getAPIPath(game) const data = await getGameUrl(apiPath) if (data?.content) { if (data?.type === PlayGameSchemeEnum.Url) { if ( provider === CasinoProviderEnum.VIVO || provider === CasinoProviderEnum.EBET ) { data.content = `${removeQueryParam(data.content, 'application')}${urlLocation}` return data } if (withoutIframe) { data.content = data.content.match(/^https?:/) ? data.content : `http://${data.content}` return data } if (provider === CasinoProviderEnum.VIVO && !urlLocation) { data.content = data.content.replace('.com', '.com/lobby/') return data } } } return data } /** * Opens a new tab or window with the constructed tab URL. * Handles Safari browser-specific behavior. * * @param {GameOptions} game - The game options including newTab configuration. */ const openTabGame = async (game: GameOptions) => { const { newTab } = game let tabNew = null const target = newTab ? '_blank' : '_self' if (isSafari) { tabNew = window.open('', target) } const data = await getTabURL(game) if (!data?.content) { tabNew?.close() onShowMaintainGame() return } if (data.type == PlayGameSchemeEnum.Url) { if (isSafari && tabNew) { tabNew.location.href = data.content } else { window.open(data.content, target) } } else { if (!tabNew) { tabNew = window.open('', '_blank') } tabNew?.document.open() tabNew?.document.write(data.content) tabNew?.document.close() } } /** * Opens a game directly within the webview environment. * It retrieves the game URL or content and directly manipulates the current window's location or document. * * @param {GameOptions} game - The game options including apiUrl, gameId, provider, etc. */ const openGameInWebview = async (game: GameOptions) => { const data = await getTabURL(game) if (!data?.content) { onShowMaintainGame() return } if (data.type == PlayGameSchemeEnum.Url) { window.location.href = data.content } else { document.open() document.write(data.content) document.close() } } // const openNewTabSafariWithHTMLContent = (content: string) => { // const a = document.createElement('a') // document.body.appendChild(a) // a.href = 'data:text/html,' + encodeURIComponent(content) // a.target = '_blank' // a.click() // document.body.removeChild(a) // } // const openNewTabSafariWithURL = (url: string) => { // const a = document.createElement('a') // document.body.appendChild(a) // a.href = url // a.target = '_blank' // a.click() // document.body.removeChild(a) // } /** * Main function to handle the game playing process. * Checks game conditions, handles specific game types, and opens the game. * * @param {GameOptions} game - The game options including various configurations. */ const playGame = async (game: GameOptions): Promise => { gamesStore.setLastGame(game) if (game.isMaintenance) { onShowMaintainGame() return } if (checkCommingSoon(game)) { onShowCommingSoon() return } if (checkMaintainProvider(game)) { openMaintainProvider() return } if (checkMaintainAthenaGames(game)) { openModalMaintainAthenaGames() return } if (checkLogin(game)) { setOpenAuthModal(true) return } if (checkPromotion(game)) { onShowPromotionPrevent() return } if (checkBalance(game)) { onShowNotEnoughBalance() return } if (checkAndPlayIFameGame(game)) { return } if (isPlayingInWebview()) { openGameInWebview(game) return } game.apiUrl = '' // not used any more await openTabGame(game) } /** * Gets the home URL based on the current route and site domain configuration. * * @returns {string} The constructed home URL. */ const getHomeURL = (): string => { return ( useRuntimeConfig().public.SITE_DOMAIN.replace(/\/$/, '') + route.fullPath ) } /** * Plays a PGSoft game by requesting its HTML content and displaying it. * * @param {string} gameId - The ID of the PGSoft game. */ // const playPGSoftGame = async (gameId: string) => { // const params = { // url_type: 'game-entry', // path: `/${gameId}/index.html`, // extra_args: { // btt: 1, // l: 'vi', // }, // } // const response = await getPGSoftGameHTML(params) // if (response.value) { // if (isSafari) { // openNewTabSafariWithHTMLContent(response.value.data) // } else { // const newTab = window.open('', '_blank') // if (newTab) { // newTab.document.open() // newTab.document.write(response.value.data) // newTab.document.close() // } // } // } else { // onShowMaintainGame() // } // } /** * Constructs the game path based on API URL, game ID, and provider. * * @param {string} apiUrl - The base API URL. * @param {string} gameId - The ID of the game. * @param {string} provider - The provider of the game. * @returns {string} The constructed game path. */ const getGamePath = ( apiUrl: string, gameId: string, provider: string, ID?: number, ): string => { if (!gameId) { return apiUrl } const params = new URLSearchParams({ gameName: gameId, id: ID?.toString() ?? '', provider, }) if ( [ GameProviderEnum.Jili, GameProviderEnum.Fachai, GameProviderEnum.Pragmatic, ].includes(provider as GameProviderEnum) ) { params.set('gameId', gameId) params.set('gameName', provider) } return `${apiUrl}?${params.toString()}` } /** * Opens an absolute URL for a game, either in a new tab or the same window. * * @param {string} gameOpenUrl - The URL to open. * @param {string} gameUrl - The game URL. * @param {boolean} [isNewTab] - Optional flag indicating if the URL should be opened in a new tab. */ const openGameAbsoluteUrl = ( gameOpenUrl: string, gameUrl: string, isNewTab?: boolean, ): void => { if (isNewTab && gameOpenUrl) { if (isPlayingInWebview()) { window.location.href = gameOpenUrl return } window.open(gameOpenUrl, '_blank') } else { window.location.href = gameUrl } } /** * Opens a new tab with the specified URL and features. * * @param {string} url - The URL to open in a new tab. * @param {string} [features] - Optional features for the new tab. * @returns {Window | null} The new window object or null if the tab could not be opened. */ const openNewTab = (url: string, features = '') => { return window.open(url, '_blank', features) } /** * Gets the URL for a game based on its type (casino or non-casino). * * @param {string} url - The base URL of the game. * @param {boolean} isCasino - Flag indicating if the game is a casino game. * @returns {Promise} The constructed game URL based on type. */ const getGameUrlBasedOnType = async (url: string, isCasino: boolean) => { const apiUrl = getIframeGameMapGameApi(url) || url return isCasino ? await getGameCasinoUrl(apiUrl) : await getGameUrl(apiUrl) } /** * Opens a relative URL for a game, handling special cases such as specific game URLs or mobile devices. * * @param {string} gameUrl - The relative game URL. * @param {boolean} isNewTab - Flag indicating if the URL should be opened in a new tab. * @param {string} provider - The provider of the game. * @param {boolean} [isCasino=false] - Optional flag indicating if the game is a casino game. * @returns {Promise} */ const openGameRelativeUrl = async ( gameUrl: string, isNewTab: boolean, provider: string, isCasino = false, ): Promise => { if (gameUrl === '/numbergame?sportId=soccerlottery') { if (isPlayingInWebview()) { const gameUrls = await getGameUrl( getIframeGameMapGameApi(gameUrl) || gameUrl, ) if (gameUrls?.content) { window.location.href = gameUrls.content! } return } const newTabOpened = openNewTab( '', 'menubar=1,resizable=1,width=414,height=666', ) if (newTabOpened) { const gameUrls = await getGameUrl( getIframeGameMapGameApi(gameUrl) || gameUrl, ) if (gameUrls) { newTabOpened.location.href = gameUrls.content! } } return } if ((!gameUrl.includes('http') && isMobile && isNewTab) || isCasino) { if (isPlayingInWebview()) { const url = await getGameUrlBasedOnType(gameUrl, isCasino) if (url?.content) { window.location.href = url.content } return } const newTab = openNewTab('') if (newTab) { const url = await getGameUrlBasedOnType(gameUrl, isCasino) if (url) { newTab.location.href = url.content } else { newTab.close() } } } else if (isNewTab) { if (isPlayingInWebview()) { window.location.href = gameUrl return } openNewTab(gameUrl) } else { void router.push(gameUrl) } } /** * Gets the API URL for an iframe game based on the game URL or route slug. * * @param {string} [url=''] - The URL or slug of the game. * @returns {string} The API URL for the iframe game. */ const getIframeGameMapGameApi = (url = ''): string => { const gameUrl = url || (route.params?.slug as string) const params = gameUrl.split('?') const paramIsMobile: string = isMobile.toString() const apiURL = IFRAME_GAME_API_MAP[params[0] as IFrameGameURLEnum] switch (params[0]) { case IFrameGameURLEnum.C_SPORTS: { const searchParams = new URLSearchParams(params[1]) const mid = searchParams.get('mid') if (mid) { return `${apiURL}&matchId=${mid}&isMobile=${paramIsMobile}` } return `${apiURL}&isMobile=${paramIsMobile}` } case IFrameGameURLEnum.V_GAMING: case IFrameGameURLEnum.VIRTUAL_GAME: case IFrameGameURLEnum.VIRTUAL_CASINO: case IFrameGameURLEnum.SABA_CLUB: case IFrameGameURLEnum.KENO: case IFrameGameURLEnum.LOTTERY: case IFrameGameURLEnum.TABLE_GAMES: case IFrameGameURLEnum.NUMBER_GAME: { const { provider, gameId, sportId, categoryId } = getQueryParamObjectFromPath(url) const queryParams = new URLSearchParams({ provider: provider || '', gameId: gameId || '', ...(sportId ? { sportId } : {}), ...(categoryId && !sportId ? { categoryId } : {}), }) return `${apiURL}&${queryParams.toString()}` } case IFrameGameURLEnum.K_SPORTS: { const { eventId, leagueId } = getQueryParamObjectFromPath(url) const params = new URLSearchParams({ isMobile: paramIsMobile, eventId: eventId, leagueId: leagueId, }) return `${apiURL}&${params.toString()}` } case IFrameGameURLEnum.LIVE_CASINO: { const { category, tableId } = route.query return `${apiURL}?category=${ category as string }&tableId=${tableId as string}` } case IFrameGameURLEnum.ESPORTS: case IFrameGameURLEnum.VIRTUAL_SPORTS: case IFrameGameURLEnum.P_SPORTS: return `${apiURL}&isMobile=${paramIsMobile}` case IFrameGameURLEnum.M_SPORTS: case IFrameGameURLEnum.I_SPORTS: return apiURL default: return '' } } /** * Fetches the game URL from the API. * * @param {string} apiUrl - The API URL to fetch the game URL from. * @param {boolean} [isDeviceMobile=isMobile] - Optional flag indicating if the device is mobile. * @returns {Promise} The game URL fetched from the API. */ const getGameUrl = async ( apiUrl: string, isDeviceMobile: boolean = isMobile, ) => { const paths = apiUrl.split('?') const params = new URLSearchParams(paths[1]) try { const data = await game.launch< Record, PlayGameApiReponse >(paths[0], { ...Object.fromEntries(params), isMobile: isDeviceMobile, }) const gameUrlContent = { ...data, content: data.content?.replace('undefined', ''), } if (!gameUrlContent.content) { throw new Error('Game URL not found') } return gameUrlContent } catch (ex) { console.error(' ~ getGameUrl ~ ex:', ex) onShowMaintainGame() } } /** * Fetches the casino game URL from the API. * * @param {string} apiUrl - The API URL to fetch the casino game URL from. * @returns {Promise} The casino game URL fetched from the API. */ const getGameCasinoUrl = async (apiUrl: string) => { const data = await game.launch(apiUrl) return data } return { lastGame, playGame, getGameUrl, getIframeGameMapGameApi, getGameUrlIframe, openModalMaintainAthenaGames, } }