@usions/sdk 2.0.2 → 2.1.0
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.
- package/package.json +16 -5
- package/src/browser.js +1392 -1139
- package/src/index.js +2 -47
- package/src/modules/back-button.js +26 -0
- package/src/modules/bot.js +52 -0
- package/src/modules/chat.js +35 -0
- package/src/modules/core.js +207 -0
- package/src/modules/file-storage.js +46 -0
- package/src/modules/game-core.js +149 -0
- package/src/modules/game-direct.js +219 -0
- package/src/modules/game-methods.js +334 -0
- package/src/modules/game-proxy.js +149 -0
- package/src/modules/game-socket.js +195 -0
- package/src/modules/index.js +54 -0
- package/src/modules/misc.js +123 -0
- package/src/modules/results.js +34 -0
- package/src/modules/session.js +62 -0
- package/src/modules/storage.js +64 -0
- package/src/modules/ui.js +111 -0
- package/src/modules/user.js +61 -0
- package/src/modules/wallet.js +114 -0
- package/types/index.d.ts +62 -2
package/src/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @usions/sdk — ES module
|
|
2
|
+
* @usions/sdk — ES module entry point for the Usion Mini App SDK
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
5
|
* import Usion from '@usions/sdk';
|
|
@@ -7,49 +7,4 @@
|
|
|
7
7
|
* import { Usion } from '@usions/sdk';
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
const _global = {};
|
|
12
|
-
|
|
13
|
-
// Execute the IIFE SDK with our synthetic global
|
|
14
|
-
(function (global) {
|
|
15
|
-
'use strict';
|
|
16
|
-
|
|
17
|
-
// ---- Begin inlined SDK bootstrap ----
|
|
18
|
-
// We re-export the Usion object that the IIFE creates.
|
|
19
|
-
// Rather than duplicating the full SDK source, we dynamically load it.
|
|
20
|
-
// For the npm module path, consumers import this file;
|
|
21
|
-
// for script-tag usage, they load browser.js directly.
|
|
22
|
-
// ---- End inlined SDK bootstrap ----
|
|
23
|
-
|
|
24
|
-
// Attach a marker so consumers can detect the module version
|
|
25
|
-
global.__USION_SDK_MODULE__ = true;
|
|
26
|
-
})(_global);
|
|
27
|
-
|
|
28
|
-
// Dynamic import approach: read the IIFE SDK and evaluate it
|
|
29
|
-
import { readFileSync } from 'fs';
|
|
30
|
-
import { fileURLToPath } from 'url';
|
|
31
|
-
import { dirname, join } from 'path';
|
|
32
|
-
|
|
33
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
34
|
-
const __dirname = dirname(__filename);
|
|
35
|
-
|
|
36
|
-
// Create a minimal global-like object for the IIFE
|
|
37
|
-
const _sdkGlobal = {};
|
|
38
|
-
const sdkSource = readFileSync(join(__dirname, 'browser.js'), 'utf-8');
|
|
39
|
-
|
|
40
|
-
// Replace the IIFE's global reference to capture the Usion object
|
|
41
|
-
const wrappedSource = sdkSource
|
|
42
|
-
.replace(
|
|
43
|
-
/\}\)\(typeof window !== 'undefined' \? window : this\);?\s*$/,
|
|
44
|
-
'})(_sdkGlobal);'
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
// Execute in a function scope to avoid polluting
|
|
48
|
-
const execFn = new Function('_sdkGlobal', wrappedSource);
|
|
49
|
-
execFn(_sdkGlobal);
|
|
50
|
-
|
|
51
|
-
/** The Usion SDK instance */
|
|
52
|
-
const Usion = _sdkGlobal.Usion;
|
|
53
|
-
|
|
54
|
-
export { Usion };
|
|
55
|
-
export default Usion;
|
|
10
|
+
export { default as Usion, default } from './modules/index.js';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usion SDK Back Button — claim/release host back button for in-app navigation
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function createBackButtonMethods(Usion) {
|
|
6
|
+
return {
|
|
7
|
+
/**
|
|
8
|
+
* Claim the host app's back button for one-time in-app navigation.
|
|
9
|
+
* When claimed, pressing back sends BACK_BUTTON_PRESSED to the mini app
|
|
10
|
+
* instead of closing it. Automatically resets after one press.
|
|
11
|
+
* @param {function} callback - Called when the user presses the claimed back button
|
|
12
|
+
*/
|
|
13
|
+
claimBackButton: function(callback) {
|
|
14
|
+
Usion._backButtonCallback = callback;
|
|
15
|
+
Usion._post({ type: 'CLAIM_BACK_BUTTON' });
|
|
16
|
+
},
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Release a previously claimed back button, restoring default close behavior.
|
|
20
|
+
*/
|
|
21
|
+
releaseBackButton: function() {
|
|
22
|
+
Usion._backButtonCallback = null;
|
|
23
|
+
Usion._post({ type: 'RELEASE_BACK_BUTTON' });
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usion SDK Bot Bridge — for inline bot iframes
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function createBotModule(Usion) {
|
|
6
|
+
return {
|
|
7
|
+
_messageHandler: null,
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Call a bot action. Delivered as an "iframe_action" webhook to the bot's server.
|
|
11
|
+
* @param {string} action - Action name (e.g., "submit_form", "select_item")
|
|
12
|
+
* @param {object} [data] - Action payload
|
|
13
|
+
* @returns {Promise} Resolves when the host app confirms delivery
|
|
14
|
+
*/
|
|
15
|
+
callAction: function(action, data) {
|
|
16
|
+
return Usion._request('CALL_BOT', { action: action, data: data || {} }, 30000);
|
|
17
|
+
},
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Send a message as if the user typed it. Triggers the normal bot webhook flow.
|
|
21
|
+
* @param {string} text - Message text
|
|
22
|
+
*/
|
|
23
|
+
sendMessage: function(text) {
|
|
24
|
+
Usion._post({ type: 'SEND_USER_MESSAGE', text: text });
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Update context metadata visible to the bot on the next webhook delivery.
|
|
29
|
+
* @param {object} ctx - Context key/value pairs
|
|
30
|
+
*/
|
|
31
|
+
updateContext: function(ctx) {
|
|
32
|
+
Usion._post({ type: 'UPDATE_CONTEXT', context: ctx });
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Close the iframe, optionally returning a result to the bot.
|
|
37
|
+
* @param {object} [result] - Optional result data
|
|
38
|
+
*/
|
|
39
|
+
close: function(result) {
|
|
40
|
+
Usion._post({ type: 'CLOSE', result: result });
|
|
41
|
+
},
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Listen for new bot messages in the conversation.
|
|
45
|
+
* Called whenever the bot sends a message (text, components, etc.).
|
|
46
|
+
* @param {function} callback - Called with { id, content, content_type, components, sender_id }
|
|
47
|
+
*/
|
|
48
|
+
onMessage: function(callback) {
|
|
49
|
+
this._messageHandler = callback;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usion SDK Chat Module — messaging between users
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {object} Usion - Reference to the main Usion object
|
|
7
|
+
*/
|
|
8
|
+
export function createChatModule(Usion) {
|
|
9
|
+
return {
|
|
10
|
+
/**
|
|
11
|
+
* Request to send a message to another user.
|
|
12
|
+
* The parent app will show a confirmation prompt to the user.
|
|
13
|
+
* @param {string} recipientId - Usion user ID of the recipient
|
|
14
|
+
* @param {string} message - Message content to send
|
|
15
|
+
* @returns {Promise<{success: boolean, reason?: string}>}
|
|
16
|
+
*/
|
|
17
|
+
sendMessage: function(recipientId, message) {
|
|
18
|
+
return Usion._request('SEND_MESSAGE_REQUEST', {
|
|
19
|
+
recipientId: recipientId,
|
|
20
|
+
message: message
|
|
21
|
+
});
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create a personal chat with another user (no message sent).
|
|
26
|
+
* @param {string} peerUserId - Usion user ID of the other user
|
|
27
|
+
* @returns {Promise<{chatId: string, peerName: string, peerUsername: string, peerAvatar: string}>}
|
|
28
|
+
*/
|
|
29
|
+
createPersonalChat: function(peerUserId) {
|
|
30
|
+
return Usion._request('CREATE_PERSONAL_CHAT', {
|
|
31
|
+
peerUserId: peerUserId
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usion SDK Core — init, _post, _request, message handling
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Request ID counter for tracking async responses
|
|
6
|
+
let _requestId = 0;
|
|
7
|
+
export const _pendingRequests = {};
|
|
8
|
+
|
|
9
|
+
export function getNextRequestId() {
|
|
10
|
+
return ++_requestId;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Core Usion object with init, _post, _request
|
|
15
|
+
*/
|
|
16
|
+
export const core = {
|
|
17
|
+
version: '2.1.0',
|
|
18
|
+
config: {},
|
|
19
|
+
_initialized: false,
|
|
20
|
+
_initCallback: null,
|
|
21
|
+
_messageHandlerRegistered: false,
|
|
22
|
+
_results: [],
|
|
23
|
+
_backButtonCallback: null,
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Initialize the SDK with config from parent app
|
|
27
|
+
* @param {function} callback - Called with config when ready
|
|
28
|
+
*/
|
|
29
|
+
init: function(callback) {
|
|
30
|
+
const self = this;
|
|
31
|
+
|
|
32
|
+
// Prevent double initialization - just update callback
|
|
33
|
+
if (self._initialized) {
|
|
34
|
+
if (callback) callback(self.config);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Store callback for when config arrives
|
|
39
|
+
self._initCallback = callback;
|
|
40
|
+
|
|
41
|
+
// Only register message handler once
|
|
42
|
+
if (self._messageHandlerRegistered) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
self._messageHandlerRegistered = true;
|
|
46
|
+
|
|
47
|
+
// Setup global message handler
|
|
48
|
+
window.addEventListener('message', function(event) {
|
|
49
|
+
let data;
|
|
50
|
+
try {
|
|
51
|
+
data = typeof event.data === 'string' ? JSON.parse(event.data) : event.data;
|
|
52
|
+
} catch (e) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Handle INIT message
|
|
57
|
+
if (data.type === 'INIT' && data.config) {
|
|
58
|
+
// Prevent double config - only set once
|
|
59
|
+
if (self._initialized) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
self.config = data.config;
|
|
64
|
+
self._initialized = true;
|
|
65
|
+
// We received INIT from a parent -> we are embedded (iframe or WebView)
|
|
66
|
+
self._isEmbedded = true;
|
|
67
|
+
|
|
68
|
+
// Initialize user module with config data
|
|
69
|
+
if (data.config.userId) {
|
|
70
|
+
self.user._id = data.config.userId;
|
|
71
|
+
self.user._name = data.config.userName;
|
|
72
|
+
self.user._avatar = data.config.userAvatar;
|
|
73
|
+
self.user._token = data.config.authToken;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Initialize session module
|
|
77
|
+
if (data.config.sessionId) {
|
|
78
|
+
self.session._id = data.config.sessionId;
|
|
79
|
+
self.session._data = data.config.sessionData || {};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Initialize wallet with balance if provided
|
|
83
|
+
if (data.config.balance !== undefined) {
|
|
84
|
+
self.wallet._balance = data.config.balance;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Initialize results from server
|
|
88
|
+
if (data.config.results) {
|
|
89
|
+
self._results = data.config.results;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Call the stored init callback
|
|
93
|
+
if (self._initCallback) {
|
|
94
|
+
self._initCallback(data.config);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Handle response messages for async requests
|
|
99
|
+
if (data._requestId && _pendingRequests[data._requestId]) {
|
|
100
|
+
const { resolve, reject } = _pendingRequests[data._requestId];
|
|
101
|
+
delete _pendingRequests[data._requestId];
|
|
102
|
+
|
|
103
|
+
if (data.error) {
|
|
104
|
+
reject(new Error(data.error));
|
|
105
|
+
} else {
|
|
106
|
+
resolve(data);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Handle balance updates
|
|
111
|
+
if (data.type === 'BALANCE_UPDATE') {
|
|
112
|
+
self.wallet._balance = data.balance;
|
|
113
|
+
if (self.wallet._balanceChangeHandler) {
|
|
114
|
+
self.wallet._balanceChangeHandler(data.balance);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Handle back button pressed (one-time claim)
|
|
119
|
+
if (data.type === 'BACK_BUTTON_PRESSED' && self._backButtonCallback) {
|
|
120
|
+
var cb = self._backButtonCallback;
|
|
121
|
+
self._backButtonCallback = null; // one-time use
|
|
122
|
+
cb();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Handle bot messages forwarded from host app
|
|
126
|
+
if (data.type === 'BOT_MESSAGE' && self.bot && self.bot._messageHandler) {
|
|
127
|
+
self.bot._messageHandler(data.message);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
// Signal ready to parent
|
|
132
|
+
this._post({ type: 'READY' });
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Get the current theme ('light' or 'dark')
|
|
137
|
+
* @returns {string}
|
|
138
|
+
*/
|
|
139
|
+
getTheme: function() {
|
|
140
|
+
return this.config.theme || 'light';
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get the current language/locale (e.g. 'en', 'mn')
|
|
145
|
+
* @returns {string}
|
|
146
|
+
*/
|
|
147
|
+
getLanguage: function() {
|
|
148
|
+
return this.config.language || 'en';
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Send message to parent app
|
|
153
|
+
* @private
|
|
154
|
+
*/
|
|
155
|
+
_post: function(message) {
|
|
156
|
+
const msg = JSON.stringify(message);
|
|
157
|
+
|
|
158
|
+
// React Native WebView
|
|
159
|
+
if (window.ReactNativeWebView) {
|
|
160
|
+
window.ReactNativeWebView.postMessage(msg);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Web iframe
|
|
165
|
+
if (window.parent !== window) {
|
|
166
|
+
window.parent.postMessage(message, '*');
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Send async request to parent and wait for response
|
|
172
|
+
* @private
|
|
173
|
+
*/
|
|
174
|
+
_request: function(type, data, timeout) {
|
|
175
|
+
const self = this;
|
|
176
|
+
timeout = timeout || 5000;
|
|
177
|
+
|
|
178
|
+
return new Promise(function(resolve, reject) {
|
|
179
|
+
const requestId = getNextRequestId();
|
|
180
|
+
|
|
181
|
+
// Setup timeout
|
|
182
|
+
const timer = setTimeout(function() {
|
|
183
|
+
delete _pendingRequests[requestId];
|
|
184
|
+
reject(new Error('Request timeout'));
|
|
185
|
+
}, timeout);
|
|
186
|
+
|
|
187
|
+
// Store pending request
|
|
188
|
+
_pendingRequests[requestId] = {
|
|
189
|
+
resolve: function(result) {
|
|
190
|
+
clearTimeout(timer);
|
|
191
|
+
resolve(result);
|
|
192
|
+
},
|
|
193
|
+
reject: function(error) {
|
|
194
|
+
clearTimeout(timer);
|
|
195
|
+
reject(error);
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
// Send request
|
|
200
|
+
self._post({
|
|
201
|
+
type: type,
|
|
202
|
+
_requestId: requestId,
|
|
203
|
+
...data
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usion SDK File Storage — large binary data (IndexedDB / filesystem)
|
|
3
|
+
*
|
|
4
|
+
* Scoped per-user, per-service like regular storage.
|
|
5
|
+
* Uses IndexedDB (web) or filesystem (mobile) — no localStorage size limits.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export function createFileStorageModule(sdk) {
|
|
9
|
+
return {
|
|
10
|
+
/**
|
|
11
|
+
* Store a file (base64 encoded)
|
|
12
|
+
* @param {string} key - Storage key
|
|
13
|
+
* @param {string} base64Data - Base64-encoded file content (no data: prefix)
|
|
14
|
+
* @param {string} mimeType - MIME type (e.g. 'image/png')
|
|
15
|
+
* @returns {Promise<void>}
|
|
16
|
+
*/
|
|
17
|
+
set: function(key, base64Data, mimeType) {
|
|
18
|
+
return sdk._request('FILE_STORAGE_SET', {
|
|
19
|
+
key: key,
|
|
20
|
+
base64Data: base64Data,
|
|
21
|
+
mimeType: mimeType || 'application/octet-stream'
|
|
22
|
+
}, 30000).then(function() { return; });
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get a stored file
|
|
27
|
+
* @param {string} key - Storage key
|
|
28
|
+
* @returns {Promise<{base64Data: string, mimeType: string} | null>}
|
|
29
|
+
*/
|
|
30
|
+
get: function(key) {
|
|
31
|
+
return sdk._request('FILE_STORAGE_GET', { key: key }, 30000).then(function(response) {
|
|
32
|
+
if (!response || !response.base64Data) return null;
|
|
33
|
+
return { base64Data: response.base64Data, mimeType: response.mimeType };
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Remove a stored file
|
|
39
|
+
* @param {string} key - Storage key
|
|
40
|
+
* @returns {Promise<void>}
|
|
41
|
+
*/
|
|
42
|
+
remove: function(key) {
|
|
43
|
+
return sdk._request('FILE_STORAGE_REMOVE', { key: key }).then(function() { return; });
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Usion SDK Game Core — game module base, connect routing, event registrations
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { applyGameDirect } from './game-direct.js';
|
|
6
|
+
import { applyGameSocket } from './game-socket.js';
|
|
7
|
+
import { applyGameProxy } from './game-proxy.js';
|
|
8
|
+
import { applyGameMethods } from './game-methods.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create the game module with all sub-modules applied
|
|
12
|
+
* @param {object} Usion - Reference to the main Usion object
|
|
13
|
+
*/
|
|
14
|
+
export function createGameModule(Usion) {
|
|
15
|
+
const game = {
|
|
16
|
+
socket: null,
|
|
17
|
+
directSocket: null,
|
|
18
|
+
roomId: null,
|
|
19
|
+
playerId: null,
|
|
20
|
+
connected: false,
|
|
21
|
+
directMode: false,
|
|
22
|
+
directConfig: null,
|
|
23
|
+
_directSeq: 0,
|
|
24
|
+
_eventHandlers: {},
|
|
25
|
+
_lastSequence: 0,
|
|
26
|
+
_connecting: false,
|
|
27
|
+
_connectPromise: null,
|
|
28
|
+
_joined: false,
|
|
29
|
+
_joinPromise: null,
|
|
30
|
+
_useProxy: false,
|
|
31
|
+
_proxyListenerSetup: false,
|
|
32
|
+
_heartbeatInterval: null,
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Connect to the game socket server
|
|
36
|
+
* @param {string} socketUrl - Socket.IO server URL (optional, uses config)
|
|
37
|
+
* @param {string} token - JWT auth token (optional, uses user.getToken())
|
|
38
|
+
* @returns {Promise} Resolves when connected
|
|
39
|
+
*/
|
|
40
|
+
connect: function(socketUrl, token) {
|
|
41
|
+
const self = this;
|
|
42
|
+
var connectionMode = (Usion.config && Usion.config.connectionMode) || 'platform';
|
|
43
|
+
if (connectionMode === 'direct') {
|
|
44
|
+
return self.connectDirect();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Use config values as defaults
|
|
48
|
+
socketUrl = socketUrl || Usion.config.socketUrl;
|
|
49
|
+
token = token || Usion.user.getToken();
|
|
50
|
+
|
|
51
|
+
if (!socketUrl) {
|
|
52
|
+
return Promise.reject(new Error('No socket URL provided'));
|
|
53
|
+
}
|
|
54
|
+
if (!token) {
|
|
55
|
+
return Promise.reject(new Error('No auth token available'));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// If already connected (direct or proxy), return immediately
|
|
59
|
+
if (self._useProxy && self.connected) {
|
|
60
|
+
return Promise.resolve();
|
|
61
|
+
}
|
|
62
|
+
if (self.socket && self.connected) {
|
|
63
|
+
return Promise.resolve();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// If currently connecting, return the existing promise
|
|
67
|
+
if (self._connecting && self._connectPromise) {
|
|
68
|
+
return self._connectPromise;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// When running inside an iframe or WebView, use parent as socket proxy
|
|
72
|
+
var isInFrame = !!window.__USION_PROXY__
|
|
73
|
+
|| window.parent !== window
|
|
74
|
+
|| !!window.ReactNativeWebView
|
|
75
|
+
|| !!Usion._isEmbedded;
|
|
76
|
+
|
|
77
|
+
if (isInFrame) {
|
|
78
|
+
Usion.log('Running in iframe \u2013 using parent app as socket proxy');
|
|
79
|
+
return self._connectViaProxy();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
self._connecting = true;
|
|
83
|
+
self._connectPromise = new Promise(function(resolve, reject) {
|
|
84
|
+
// Check if socket.io-client is available
|
|
85
|
+
if (typeof io === 'undefined') {
|
|
86
|
+
// Load socket.io client
|
|
87
|
+
var script = document.createElement('script');
|
|
88
|
+
script.src = '/socket.io.min.js';
|
|
89
|
+
script.onload = function() {
|
|
90
|
+
self._initSocket(socketUrl, token, resolve, reject);
|
|
91
|
+
};
|
|
92
|
+
script.onerror = function() {
|
|
93
|
+
// Local file not available, try CDN as fallback
|
|
94
|
+
var cdnScript = document.createElement('script');
|
|
95
|
+
cdnScript.src = 'https://cdn.socket.io/4.7.2/socket.io.min.js';
|
|
96
|
+
cdnScript.onload = function() {
|
|
97
|
+
self._initSocket(socketUrl, token, resolve, reject);
|
|
98
|
+
};
|
|
99
|
+
cdnScript.onerror = function() {
|
|
100
|
+
self._connecting = false;
|
|
101
|
+
reject(new Error('Failed to load Socket.IO client'));
|
|
102
|
+
};
|
|
103
|
+
document.head.appendChild(cdnScript);
|
|
104
|
+
};
|
|
105
|
+
document.head.appendChild(script);
|
|
106
|
+
} else {
|
|
107
|
+
self._initSocket(socketUrl, token, resolve, reject);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return self._connectPromise;
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
// Event handler registrations
|
|
115
|
+
onJoined: function(callback) { this._eventHandlers.joined = callback; },
|
|
116
|
+
onPlayerJoined: function(callback) { this._eventHandlers.playerJoined = callback; },
|
|
117
|
+
onPlayerLeft: function(callback) { this._eventHandlers.playerLeft = callback; },
|
|
118
|
+
onStateUpdate: function(callback) { this._eventHandlers.stateUpdate = callback; },
|
|
119
|
+
onSync: function(callback) { this._eventHandlers.sync = callback; },
|
|
120
|
+
onAction: function(callback) { this._eventHandlers.action = callback; },
|
|
121
|
+
onRealtime: function(callback) { this._eventHandlers.realtime = callback; },
|
|
122
|
+
onGameFinished: function(callback) { this._eventHandlers.finished = callback; },
|
|
123
|
+
onGameRestarted: function(callback) { this._eventHandlers.restarted = callback; },
|
|
124
|
+
onError: function(callback) { this._eventHandlers.error = callback; },
|
|
125
|
+
onRematchRequest: function(callback) { this._eventHandlers.rematchRequest = callback; },
|
|
126
|
+
onDisconnect: function(callback) { this._eventHandlers.disconnect = callback; },
|
|
127
|
+
onReconnect: function(callback) { this._eventHandlers.reconnect = callback; },
|
|
128
|
+
onConnectionError: function(callback) { this._eventHandlers.connectionError = callback; },
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Register a generic event handler
|
|
132
|
+
* @param {string} event - Event name
|
|
133
|
+
* @param {function} callback - Handler function
|
|
134
|
+
*/
|
|
135
|
+
on: function(event, callback) {
|
|
136
|
+
if (this.socket) {
|
|
137
|
+
this.socket.on(event, callback);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Apply sub-modules
|
|
143
|
+
applyGameDirect(game, Usion);
|
|
144
|
+
applyGameSocket(game, Usion);
|
|
145
|
+
applyGameProxy(game, Usion);
|
|
146
|
+
applyGameMethods(game, Usion);
|
|
147
|
+
|
|
148
|
+
return game;
|
|
149
|
+
}
|