@goodgamestudios/cxf-webshop 6.27.3 → 7.0.0-qa.1

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.
@@ -0,0 +1,207 @@
1
+ import { inject, Reader } from 'readuz'
2
+ import { CxfEvents, IGameEvent, ILoginData } from '@goodgamestudios/cxf-events'
3
+ import { DIReader, WebshopEvents, IDictionary, IPushMessageData } from '../common'
4
+ import { combineReaders } from '../combineReaders'
5
+
6
+ export type SubscribeToCxf = Reader<void, void>
7
+ const commonCxfReducer: DIReader<SubscribeToCxf> = inject(
8
+ (environment) => environment.log,
9
+ (environment) => environment.getCxf,
10
+ (environment) => environment.onOpen,
11
+ (environment) => environment.tryCatch,
12
+ (environment) => environment.pushHandlers,
13
+ (log, getCxf, onOpen, tryCatch, pushHandlers) => () => {
14
+ const cxf = getCxf()
15
+ cxf.on(CxfEvents.OpenIGS, tryCatch(onOpen))
16
+ cxf.on(
17
+ CxfEvents.Push,
18
+ tryCatch(({ id, payload }: IPushMessageData) => {
19
+ log('cxf.push', id, payload)
20
+ const handler = pushHandlers[id]
21
+ handler && handler(payload)
22
+ })
23
+ )
24
+ }
25
+ )
26
+
27
+ export type CxfReducers = IDictionary<SubscribeToCxf>
28
+
29
+ export function createCxfReducers(handlers: IDictionary<DIReader<SubscribeToCxf>>) {
30
+ return combineReaders({
31
+ common: commonCxfReducer,
32
+ gameEvents: subscribeToGameEvents,
33
+ ...handlers,
34
+ })
35
+ }
36
+
37
+ export type SubscribeToGameEvents = () => void
38
+ export const subscribeToGameEvents: DIReader<SubscribeToGameEvents> = inject(
39
+ (environment) => environment.log,
40
+ (environment) => environment.logError,
41
+ (environment) => environment.getCxf,
42
+ (environment) => environment.getStore,
43
+ (environment) => environment.setStore,
44
+ (environment) => environment.fetchUnreadOfferNotificationsCount,
45
+ (log, logError, getCxf, getStore, setStore, fetchUnreadOfferNotificationsCount) => () => {
46
+ const cxf = getCxf()
47
+ cxf.on(CxfEvents.Login, (e: ILoginData) => {
48
+ const { gameEvents } = getStore()
49
+ const { gameEvents: eventsInLogin } = e
50
+ // eslint-disable-next-line unicorn/consistent-function-scoping
51
+ const eventNotExistPredicateGenerator = (existingEvents: IGameEvent[]) => (event: IGameEvent) =>
52
+ !existingEvents.map((event_) => event_.type).includes(event.type)
53
+ setStore({
54
+ ...e,
55
+ gameEvents: [...gameEvents.filter(eventNotExistPredicateGenerator(eventsInLogin)), ...eventsInLogin],
56
+ })
57
+ log(CxfEvents.Login, 'reducer', getStore())
58
+ fetchUnreadOfferNotificationsCount()
59
+ })
60
+
61
+ cxf.on(CxfEvents.GameEventUpdate, (e: IGameEvent[]) => {
62
+ log(CxfEvents.GameEventUpdate, e)
63
+ const store = getStore()
64
+ const updatedEventTypes = new Set(e.map((event) => event.type))
65
+ setStore({
66
+ gameEvents: [...store.gameEvents.filter((event) => !updatedEventTypes.has(event.type)), ...e],
67
+ })
68
+ log(CxfEvents.GameEventUpdate, 'reducer', getStore())
69
+ })
70
+
71
+ cxf.on(CxfEvents.GameEventAdd, (e: IGameEvent) => {
72
+ log(CxfEvents.GameEventAdd, e)
73
+ const store = getStore()
74
+ setStore({
75
+ gameEvents: [...store.gameEvents, e],
76
+ })
77
+ log(CxfEvents.GameEventAdd, 'reducer', getStore())
78
+ })
79
+
80
+ cxf.on(CxfEvents.GameEventRemove, (e: number) => {
81
+ log(CxfEvents.GameEventRemove, e)
82
+ const store = getStore()
83
+ setStore({
84
+ gameEvents: store.gameEvents.filter(({ type }) => type !== e),
85
+ })
86
+ log(CxfEvents.GameEventRemove, 'reducer', getStore())
87
+ })
88
+
89
+ cxf.on(CxfEvents.LevelChanged, (level: number) => {
90
+ log(CxfEvents.LevelChanged, level)
91
+ setStore({
92
+ level,
93
+ })
94
+ log(CxfEvents.LevelChanged, 'reducer', getStore())
95
+ fetchUnreadOfferNotificationsCount()
96
+ })
97
+
98
+ cxf.on(CxfEvents.LegendLevelChanged, (legendLevel: number) => {
99
+ log(CxfEvents.LegendLevelChanged, `legendLevel: ${legendLevel}`)
100
+ setStore({
101
+ legendLevel,
102
+ })
103
+ const store = getStore()
104
+ log(CxfEvents.LegendLevelChanged, 'reducer', store)
105
+ fetchUnreadOfferNotificationsCount()
106
+ })
107
+ }
108
+ )
109
+
110
+ /*
111
+ In E4K this event is used to control the subscriptions tab
112
+ This is deprecated, as specific config values/tab visibility can be controlled using Lemonstands `config` query param
113
+ */
114
+
115
+ export const nativeSubscriptionEnableReducer: DIReader<SubscribeToCxf> = inject(
116
+ (environment) => environment.getCxf,
117
+ (environment) => environment.setStore,
118
+ (environment) => environment.preResolveConfig,
119
+ (getCxf, setStore, preResolveConfig) => () => {
120
+ const cxf = getCxf()
121
+ cxf.on(WebshopEvents.CXF_NATIVE_SUBSCRIPTION_ENABLE, (enabled: 0 | 1) => {
122
+ setStore({
123
+ subscriptionDisabled: enabled === 0,
124
+ customizationSuffix: enabled === 0 ? 'no-subscription' : '',
125
+ })
126
+ preResolveConfig()
127
+ })
128
+ }
129
+ )
130
+
131
+ /*
132
+ The config file suffix (e.g. "em.json" + "mobile" -> "em-mobile.json") can be set through an event
133
+ Used by MS and BFW integration pages
134
+ */
135
+
136
+ export const setCustomizationSuffixReducer: DIReader<SubscribeToCxf> = inject(
137
+ (environment) => environment.getCxf,
138
+ (environment) => environment.getStore,
139
+ (environment) => environment.setStore,
140
+ (environment) => environment.preResolveConfig,
141
+ (getCxf, getStore, setStore, preResolveConfig) => () => {
142
+ const cxf = getCxf()
143
+ cxf.on(WebshopEvents.CXF_SET_CUSTOMIZATION_SUFFIX, (suffix: string) => {
144
+ const store = getStore()
145
+ if (suffix === store.customizationSuffix) {
146
+ return
147
+ }
148
+ setStore({
149
+ customizationSuffix: suffix || '',
150
+ })
151
+ preResolveConfig()
152
+ })
153
+ }
154
+ )
155
+
156
+ /*
157
+ Information about whether ads are enabled or not is retrieved externally from `cxf-ad-banners`
158
+ and stored to be passed to IGS
159
+ */
160
+
161
+ export const receiveAdStatusReducer: DIReader<SubscribeToCxf> = inject(
162
+ (environment) => environment.getCxf,
163
+ (environment) => environment.setStore,
164
+ (environment) => environment.config,
165
+ (getCxf, setStore, { CXF_AD_STATUS }) =>
166
+ () => {
167
+ const cxf = getCxf()
168
+ cxf.on(CXF_AD_STATUS, ({ areBannersAvailable, bannersDetails }: Record<string, any>) => {
169
+ setStore({
170
+ adStatus: {
171
+ areBannersAvailable,
172
+ bannersDetails,
173
+ },
174
+ })
175
+ })
176
+ }
177
+ )
178
+
179
+ /*
180
+ Use a dedicated config variant on temp servers
181
+ */
182
+
183
+ export const tempServerContextReducer: DIReader<SubscribeToCxf> = inject(
184
+ (environment) => environment.getCxf,
185
+ (environment) => environment.getStore,
186
+ (environment) => environment.setStore,
187
+ (environment) => environment.preResolveConfig,
188
+ (getCxf, getStore, setStore, preResolveConfig) => () => {
189
+ const cxf = getCxf()
190
+ cxf.on(WebshopEvents.CXF_JOIN_TEMP_SERVER, (isTemp: 0 | 1) => {
191
+ const store = getStore()
192
+ setStore({
193
+ isTempServer: Boolean(isTemp),
194
+ })
195
+
196
+ // ensure suffix is not already set by the environment, e.g. BFW or MS integration
197
+ // TODO: fully migrate client detection into `cxf-webshop`
198
+ if (store.customizationSuffix === '' || store.customizationSuffix === 'temp') {
199
+ setStore({
200
+ customizationSuffix: isTemp ? 'temp' : '',
201
+ })
202
+ }
203
+
204
+ preResolveConfig()
205
+ })
206
+ }
207
+ )
@@ -0,0 +1,143 @@
1
+ /* eslint-disable unicorn/consistent-function-scoping */
2
+ import debug from 'debug'
3
+ import { inject } from 'readuz'
4
+ import { v4 as uuidv4 } from 'uuid'
5
+ import { DIReader, IDictionary } from './common'
6
+ import { parentDomain, timeout } from './utils'
7
+ import { ArgumentNullError } from './ArgumentNullError'
8
+ import { encodePing } from './ping'
9
+ import { IStore } from './store'
10
+ import { ICXF } from './cxf'
11
+
12
+ export function formatQueryString(object: IDictionary<string | number | undefined | null>) {
13
+ return Object.entries(object)
14
+ .filter(([key, value]) => value !== null && value !== undefined)
15
+ .map(([key, value]) => `${key}=${encodeURIComponent(value as string)}`)
16
+ .join('&')
17
+ }
18
+
19
+ export const getTokenAndLanguage = (store: IStore) => {
20
+ const { playerId, token, zoneId, language } = store
21
+ if (!playerId) {
22
+ throw new ArgumentNullError('playerId', playerId)
23
+ }
24
+ return {
25
+ token,
26
+ zoneId,
27
+ locale: language,
28
+ ping: encodePing({ ...store, playerId }),
29
+ }
30
+ }
31
+
32
+ export interface IProvider<T> {
33
+ get: () => T | undefined
34
+ set: (value: T) => void
35
+ }
36
+
37
+ export const getCxf = (cxfProvider: IProvider<ICXF>) => {
38
+ const cxf = cxfProvider.get()
39
+ if (!cxf) {
40
+ throw new ArgumentNullError('cxf', cxf)
41
+ }
42
+ return cxf
43
+ }
44
+
45
+ export type GetPing = () => string
46
+ export const getPing: DIReader<GetPing> = inject(
47
+ (environment) => environment.getStore,
48
+ (getStore) => () => {
49
+ const store = getStore()
50
+ const { playerId } = store
51
+ if (!playerId) {
52
+ throw new ArgumentNullError('playerId', playerId)
53
+ }
54
+ return encodePing({ ...store, playerId })
55
+ }
56
+ )
57
+
58
+ export const entityProvider = <T>(initial?: T): IProvider<T> => {
59
+ let element = initial
60
+ return {
61
+ get: () => element,
62
+ set: (value) => {
63
+ element = value
64
+ },
65
+ }
66
+ }
67
+
68
+ export type TryCatch = {
69
+ <R>(function_: () => R): () => R
70
+ <P1, R>(function_: (p1: P1) => R): (p1: P1) => R
71
+ <P1, P2, R>(function_: (p1: P1, p2: P2) => R): (p1: P1, p2: P2) => R
72
+ <P1, P2, P3, R>(function_: (p1: P1, p2: P2, p3: P3) => R): (p1: P1, p2: P2, p3: P3) => R
73
+ }
74
+ export const tryCatch: DIReader<TryCatch> = inject(
75
+ (environment) => environment.logError,
76
+ (environment) => environment.throwCxfError,
77
+ (logError_, throwAnError) =>
78
+ (function_: any) =>
79
+ (...arguments_: any[]) => {
80
+ try {
81
+ function_(...arguments_)
82
+ } catch (error) {
83
+ logError_(error)
84
+ throwAnError(error as Error)
85
+ }
86
+ }
87
+ )
88
+
89
+ export type ThrowCxfError = (e: Error) => void
90
+ export const throwCxfError: DIReader<ThrowCxfError> = inject(
91
+ (environment) => environment.config,
92
+ (environment) => environment.cxfProvider,
93
+ ({ CXF_ERROR }, cxfProvider) =>
94
+ (e) => {
95
+ const cxf = getCxf(cxfProvider)
96
+ cxf.emit(CXF_ERROR, e)
97
+ }
98
+ )
99
+
100
+ export const isArgumentNullError = (e: Error): e is ArgumentNullError => e.name === 'ArgumentNullError'
101
+
102
+ export type LoadCxf = (...arguments_: any[]) => Promise<ICXF>
103
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
104
+ export const loadCxf = timeout(() => require('@goodgamestudios/cxf-ready') as Promise<ICXF>, 10_000)
105
+
106
+ /*
107
+ * To show CXF-WEBSHOP debug messages just run in a browser console
108
+ * localStorage.debug = 'CXF-WEBSHOP:*'
109
+ */
110
+ const logger = debug('CXF-WEBSHOP')
111
+ export type Log = (...arguments_: any[]) => void
112
+ export const log: Log = (argument0: any, ...rest: any[]) => logger(argument0, ...rest)
113
+
114
+ export type LogError = (...arguments_: any[]) => void
115
+ export const logError: LogError = (...arguments_: any[]) =>
116
+ // tslint:disable-next-line:no-console
117
+ console.error('%c CXF-WEBSHOP:', 'background: #ff0000; color: #fff', ...arguments_)
118
+
119
+ export const createSessionId = () => uuidv4()
120
+
121
+ export type GetDomain = (referrer: string) => string | undefined
122
+ export const getDomain: DIReader<GetDomain> = inject(
123
+ (environment) => environment.log,
124
+ () => (referrer) => {
125
+ return parentDomain(referrer)
126
+ }
127
+ )
128
+
129
+ export type GGSGetQueryParams = () => string
130
+ export const ggsGetQueryParams: DIReader<GGSGetQueryParams> = inject(
131
+ (environment) => environment.log,
132
+ (nothing) => () => {
133
+ return ggsGetQueryParameters()
134
+ }
135
+ )
136
+
137
+ export type GGSGetReferrerValue = () => string
138
+ export const ggsGetReferrerValue: DIReader<GGSGetReferrerValue> = inject(
139
+ (environment) => environment.log,
140
+ (nothing) => () => {
141
+ return ggsGetReferrer()
142
+ }
143
+ )
@@ -0,0 +1,35 @@
1
+ import { createEnv as createEnvironment } from './env'
2
+ import { app } from './app'
3
+ import { just } from 'readuz'
4
+ import { entityProvider, log } from './helpers'
5
+ import { defaultStore } from './store'
6
+ import { createConfig } from './config'
7
+ import {
8
+ createCxfReducers,
9
+ nativeSubscriptionEnableReducer,
10
+ setCustomizationSuffixReducer,
11
+ receiveAdStatusReducer,
12
+ tempServerContextReducer,
13
+ } from './handlers/reducers'
14
+ import { ICXF } from './cxf'
15
+
16
+ if (localStorage.getItem('cxf_canvas') === 'enabled') {
17
+ log('Skip cxf-webshop loading...')
18
+ } else {
19
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, unicorn/prefer-top-level-await
20
+ require('@goodgamestudios/cxf-ready').then((cxf: ICXF) => {
21
+ app(
22
+ createEnvironment({
23
+ config: just(createConfig(cxf.gameId)),
24
+ cxfProvider: just(entityProvider(cxf)),
25
+ store: just(entityProvider(defaultStore(cxf))),
26
+ subscribeToCxf: createCxfReducers({
27
+ receiveAdStatus: receiveAdStatusReducer,
28
+ tempServerContext: tempServerContextReducer,
29
+ nativeSubscription: nativeSubscriptionEnableReducer,
30
+ setCustomizationSuffix: setCustomizationSuffixReducer,
31
+ }),
32
+ })
33
+ )
34
+ })
35
+ }
@@ -0,0 +1,3 @@
1
+ export interface IShopMessageBus {
2
+ post(message: any): void
3
+ }
@@ -0,0 +1,10 @@
1
+ import { IShopMessageBus } from './IShopMessageBus'
2
+
3
+ export class ShopMessageBus implements IShopMessageBus {
4
+ public post(message: any) {
5
+ const element = document.querySelector('#dialog') as HTMLIFrameElement
6
+ if (element && element.tagName.toLocaleLowerCase() === 'iframe') {
7
+ element.contentWindow?.postMessage(message, '*')
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,8 @@
1
+ import { IShopMessageBus } from './IShopMessageBus'
2
+
3
+ export class TestShopMessageBus implements IShopMessageBus {
4
+ public post(message: any) {
5
+ /* eslint-disable-next-line prettier/prettier */
6
+ (window as any).dialogPostMessage = JSON.stringify(message)
7
+ }
8
+ }
@@ -0,0 +1,15 @@
1
+ export interface PingProperties {
2
+ gameId: string
3
+ networkId: string
4
+ instanceId: string
5
+ playerId: string
6
+ }
7
+
8
+ export function decodePing(value: string): PingProperties {
9
+ const [gameId, networkId, instanceId, playerId] = value.split('-')
10
+ return { gameId, networkId, instanceId, playerId }
11
+ }
12
+
13
+ export function encodePing({ gameId, networkId, instanceId, playerId }: PingProperties): string {
14
+ return [gameId, networkId, instanceId, playerId].join('-')
15
+ }
@@ -0,0 +1,43 @@
1
+ import { DIReader } from './common'
2
+ import { inject } from 'readuz'
3
+
4
+ type LemonstandConfig = {
5
+ css: string
6
+ js: string
7
+ unreadOfferNotifsCountUrl: string
8
+ }
9
+
10
+ export type PreFetch = (...parameter: any[]) => void
11
+
12
+ export const preResolveConfig: DIReader<PreFetch> = inject(
13
+ (environment) => environment.log,
14
+ (environment) => environment.setStore,
15
+ (environment) => environment.createCustomizationUrl,
16
+ (log, setStore, getCustomizationUrl) => () => {
17
+ if (typeof fetch !== 'function') {
18
+ return
19
+ }
20
+ const originalCustomUrl = getCustomizationUrl()
21
+ let resolvedUrl = ''
22
+ fetch(originalCustomUrl)
23
+ .then((resp) => {
24
+ const currentCustomUrl = getCustomizationUrl()
25
+ if (currentCustomUrl === originalCustomUrl) {
26
+ // this is the latest, we keep it
27
+ resolvedUrl = resp.url
28
+ log('parsed customization url is ' + resolvedUrl)
29
+ setStore({
30
+ resolvedCustomizationUrl: resolvedUrl,
31
+ })
32
+ return resp.json()
33
+ }
34
+ })
35
+ .then((config: LemonstandConfig | undefined) => {
36
+ if (config) {
37
+ setStore({
38
+ unreadOfferNotifsCountUrl: config.unreadOfferNotifsCountUrl || '',
39
+ })
40
+ }
41
+ })
42
+ }
43
+ )
@@ -0,0 +1,32 @@
1
+ import { IDictionary } from './common'
2
+
3
+ export interface IStorageData {
4
+ growthFund: {
5
+ lastPopup: IDictionary<number>
6
+ }
7
+ }
8
+
9
+ export type GetStorageData = () => IStorageData
10
+ export const getStorageData: GetStorageData = () => {
11
+ const restoreData = () => JSON.parse(localStorage.getItem('webshop') as string) as IStorageData
12
+
13
+ if (localStorage.getItem('webshop')) {
14
+ return restoreData()
15
+ }
16
+
17
+ localStorage.setItem(
18
+ 'webshop',
19
+ JSON.stringify({
20
+ growthFund: {
21
+ lastPopup: {},
22
+ },
23
+ } as IStorageData)
24
+ )
25
+
26
+ return restoreData()
27
+ }
28
+
29
+ export type SetStorageData = (data: IStorageData) => void
30
+ export const setStorageData: SetStorageData = (data: IStorageData) => {
31
+ localStorage.setItem('webshop', JSON.stringify(data))
32
+ }
@@ -0,0 +1,82 @@
1
+ import { DIReader, IDictionary } from './common'
2
+ import { inject } from 'readuz'
3
+ import { CountryCode, IGameEvent } from '@goodgamestudios/cxf-events'
4
+ import { ICXF, IGameApi } from './cxf'
5
+
6
+ export interface IStore {
7
+ playerId?: string
8
+ instanceId: string
9
+ networkId: string
10
+ gameId: string
11
+ gameApi: IGameApi
12
+ language?: string
13
+ token?: string
14
+ zoneId?: string
15
+ xp: number
16
+ level: number
17
+ gameEvents: IGameEvent[]
18
+ countryCode: CountryCode
19
+ lastPurchaseTab: string
20
+ legendLevel?: number
21
+ subscriptionDisabled: boolean
22
+ isTempServer: boolean
23
+ customizationSuffix: string
24
+ resolvedCustomizationUrl?: string
25
+ sourceId: string
26
+ unreadOfferNotifsCountUrl: string
27
+ adStatus?: Record<string, any>
28
+ }
29
+
30
+ export function defaultStore(cxf: ICXF): IStore {
31
+ return {
32
+ playerId: cxf.playerId,
33
+ instanceId: cxf.instanceId,
34
+ networkId: cxf.networkId,
35
+ gameId: cxf.gameId,
36
+ gameApi: cxf.gameApi,
37
+ language: cxf.language,
38
+ token: cxf.token,
39
+ zoneId: cxf.zoneId,
40
+ gameEvents: [],
41
+ xp: 0,
42
+ level: 0,
43
+ legendLevel: undefined,
44
+ countryCode: '',
45
+ lastPurchaseTab: '',
46
+ subscriptionDisabled: false,
47
+ isTempServer: false,
48
+ customizationSuffix: '',
49
+ resolvedCustomizationUrl: '',
50
+ sourceId: 'unknown',
51
+ unreadOfferNotifsCountUrl: '',
52
+ }
53
+ }
54
+
55
+ export type GetStore = () => IStore
56
+ export const getStore: DIReader<GetStore> = inject(
57
+ (environment) => environment.log,
58
+ (environment) => environment.store,
59
+ (log, store) => () => {
60
+ return store.get() as IStore
61
+ }
62
+ )
63
+
64
+ export type SetStore = (data: Partial<IStore>) => void
65
+ export const setStore: DIReader<SetStore> = inject(
66
+ (environment) => environment.log,
67
+ (environment) => environment.store,
68
+ (log, store) => (data) => {
69
+ const state = store.get() as IStore
70
+ store.set({
71
+ ...state,
72
+ ...data,
73
+ })
74
+ }
75
+ )
76
+
77
+ export function criteriaSelector({ legendLevel, level }: IStore): IDictionary<any> {
78
+ return {
79
+ legendLevel,
80
+ level,
81
+ }
82
+ }
@@ -0,0 +1,59 @@
1
+ import { inject } from 'readuz'
2
+ import { DIReader, IDictionary } from './common'
3
+ import { msToSec } from './utils'
4
+ import { getCxf, getTokenAndLanguage } from './helpers'
5
+
6
+ export type IRanchWebShopCallProperties = {
7
+ eventId: number
8
+ gameId: number
9
+ playerId: number
10
+ instanceId: number
11
+ zoneId: number
12
+ networkId: number
13
+ sessionId: string
14
+ date: number
15
+ unixtimeMS: number
16
+ sourceId: string
17
+ }
18
+
19
+ export type TrackOpenAction = (sid: string) => void
20
+ export const trackOpenAction: DIReader<TrackOpenAction> = inject(
21
+ (environment) => environment.config,
22
+ (environment) => environment.trackAction,
23
+ (environment) => environment.getStore,
24
+ ({ WEB_SHOP_CALL_TRACK_ID }, trackUserAction, getStore) =>
25
+ (sid) => {
26
+ const now = Date.now()
27
+ const parameters = getTokenAndLanguage(getStore())
28
+ const { zoneId } = parameters
29
+ const sessionId = sid
30
+ const { playerId, gameId, networkId, instanceId, sourceId } = getStore()
31
+ trackUserAction({
32
+ eventId: WEB_SHOP_CALL_TRACK_ID,
33
+ date: msToSec(now),
34
+ unixtimeMS: now,
35
+ sessionId,
36
+ zoneId: zoneId ? Number.parseInt(zoneId, 10) : undefined,
37
+ playerId: Number.parseInt(playerId as string, 10),
38
+ gameId: Number.parseInt(gameId, 10),
39
+ networkId: Number.parseInt(networkId, 10),
40
+ instanceId: Number.parseInt(instanceId, 10),
41
+ sourceId,
42
+ } as IRanchWebShopCallProperties)
43
+ }
44
+ )
45
+
46
+ export interface ITrackPayload extends IDictionary<string | number> {
47
+ eventId: number
48
+ }
49
+
50
+ export type TrackAction = (payload: ITrackPayload) => void
51
+ export const trackAction: DIReader<TrackAction> = inject(
52
+ (environment) => environment.config,
53
+ (environment) => environment.cxfProvider,
54
+ ({ CXF_TRACK_MSG }, cxfProvider) =>
55
+ (payload) => {
56
+ const cxf = getCxf(cxfProvider)
57
+ cxf.emit(CXF_TRACK_MSG, payload)
58
+ }
59
+ )