276 lines
9.0 KiB
Dart
276 lines
9.0 KiB
Dart
import 'dart:convert';
|
|
|
|
class LeaderBridgeActions {
|
|
static const String getConfig = 'getConfig';
|
|
static const String pay = 'pay';
|
|
static const String closeGame = 'closeGame';
|
|
static const String loadComplete = 'loadComplete';
|
|
static const String debugLog = 'debugLog';
|
|
}
|
|
|
|
class LeaderBridgeMessage {
|
|
const LeaderBridgeMessage({
|
|
required this.action,
|
|
this.payload = const <String, dynamic>{},
|
|
});
|
|
|
|
final String action;
|
|
final Map<String, dynamic> payload;
|
|
|
|
static LeaderBridgeMessage fromAction(String action, String rawMessage) {
|
|
return LeaderBridgeMessage(
|
|
action: action,
|
|
payload: _parsePayload(rawMessage),
|
|
);
|
|
}
|
|
|
|
static LeaderBridgeMessage parse(String rawMessage) {
|
|
try {
|
|
final dynamic decoded = jsonDecode(rawMessage);
|
|
if (decoded is Map<String, dynamic>) {
|
|
return LeaderBridgeMessage(
|
|
action:
|
|
(decoded['action'] ?? decoded['cmd'] ?? decoded['event'] ?? '')
|
|
.toString()
|
|
.trim(),
|
|
payload: _parsePayload(
|
|
decoded['payload'] ?? decoded['data'] ?? decoded['params'],
|
|
),
|
|
);
|
|
}
|
|
} catch (_) {}
|
|
return LeaderBridgeMessage(action: rawMessage.trim());
|
|
}
|
|
|
|
static Map<String, dynamic> _parsePayload(dynamic rawPayload) {
|
|
if (rawPayload is Map<String, dynamic>) {
|
|
return rawPayload;
|
|
}
|
|
if (rawPayload is String) {
|
|
final trimmed = rawPayload.trim();
|
|
if (trimmed.isEmpty) {
|
|
return const <String, dynamic>{};
|
|
}
|
|
try {
|
|
final dynamic decoded = jsonDecode(trimmed);
|
|
if (decoded is Map<String, dynamic>) {
|
|
return decoded;
|
|
}
|
|
} catch (_) {}
|
|
return <String, dynamic>{'raw': trimmed};
|
|
}
|
|
return const <String, dynamic>{};
|
|
}
|
|
}
|
|
|
|
class LeaderJsBridge {
|
|
static const String channelName = 'LeaderBridgeChannel';
|
|
|
|
static String bootstrapScript({Map<String, dynamic>? launchPayload}) {
|
|
final safeLaunchPayload = jsonEncode(
|
|
launchPayload ?? const <String, dynamic>{},
|
|
);
|
|
return '''
|
|
(function() {
|
|
const launchPayload = $safeLaunchPayload;
|
|
|
|
function toPayload(input) {
|
|
if (typeof input === 'string') {
|
|
try {
|
|
return JSON.parse(input);
|
|
} catch (_) {
|
|
return input.trim() ? { raw: input.trim() } : {};
|
|
}
|
|
}
|
|
if (input && typeof input === 'object') {
|
|
return input;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
function stringifyForLog(value) {
|
|
if (value == null) {
|
|
return '';
|
|
}
|
|
if (typeof value === 'string') {
|
|
return value.length > 320 ? value.slice(0, 320) + '...' : value;
|
|
}
|
|
try {
|
|
const text = JSON.stringify(value);
|
|
return text.length > 320 ? text.slice(0, 320) + '...' : text;
|
|
} catch (_) {
|
|
return String(value);
|
|
}
|
|
}
|
|
|
|
function postMessage(action, input) {
|
|
const payload = toPayload(input);
|
|
$channelName.postMessage(JSON.stringify({
|
|
action: action,
|
|
payload: payload
|
|
}));
|
|
return payload;
|
|
}
|
|
|
|
function postDebug(tag, payload) {
|
|
postMessage('${LeaderBridgeActions.debugLog}', {
|
|
tag: tag,
|
|
message: stringifyForLog(payload)
|
|
});
|
|
}
|
|
|
|
function invokeCallback(callback, payload) {
|
|
if (typeof callback === 'function') {
|
|
callback(payload);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function dispatchConfigReady(payload) {
|
|
if (typeof window.onLeaderLaunchConfig === 'function') {
|
|
window.onLeaderLaunchConfig(payload);
|
|
}
|
|
if (typeof window.onLaunchConfig === 'function') {
|
|
window.onLaunchConfig(payload.launchConfig || {}, payload);
|
|
}
|
|
if (typeof window.dispatchEvent === 'function' && typeof CustomEvent === 'function') {
|
|
window.dispatchEvent(
|
|
new CustomEvent('leaderLaunchConfig', { detail: payload })
|
|
);
|
|
window.dispatchEvent(
|
|
new CustomEvent('launchConfig', {
|
|
detail: payload.launchConfig || {}
|
|
})
|
|
);
|
|
}
|
|
if (typeof document !== 'undefined' &&
|
|
typeof document.dispatchEvent === 'function' &&
|
|
typeof CustomEvent === 'function') {
|
|
document.dispatchEvent(
|
|
new CustomEvent('leaderLaunchConfig', { detail: payload })
|
|
);
|
|
document.dispatchEvent(
|
|
new CustomEvent('launchConfig', {
|
|
detail: payload.launchConfig || {}
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
function applyLaunchPayload(payload) {
|
|
const normalizedPayload =
|
|
payload && typeof payload === 'object' ? payload : {};
|
|
const payloadText = JSON.stringify(normalizedPayload);
|
|
const payloadChanged = window.__leaderLaunchPayloadText !== payloadText;
|
|
const launchConfig =
|
|
normalizedPayload.launchConfig &&
|
|
typeof normalizedPayload.launchConfig === 'object'
|
|
? normalizedPayload.launchConfig
|
|
: {};
|
|
const entry =
|
|
normalizedPayload.entry && typeof normalizedPayload.entry === 'object'
|
|
? normalizedPayload.entry
|
|
: {};
|
|
window.__leaderLaunchPayload = normalizedPayload;
|
|
window.__leaderLaunchPayloadText = payloadText;
|
|
window.leaderLaunchPayload = normalizedPayload;
|
|
window.__leaderLaunchConfig = launchConfig;
|
|
window.leaderLaunchConfig = launchConfig;
|
|
window.__leaderLaunchEntry = entry;
|
|
window.leaderLaunchEntry = entry;
|
|
window.NativeBridge = window.NativeBridge || {};
|
|
window.NativeBridge.config = launchConfig;
|
|
window.NativeBridge.launchConfig = launchConfig;
|
|
window.NativeBridge.entry = entry;
|
|
window.NativeBridge.getConfig = function(callback) {
|
|
invokeCallback(callback, launchConfig);
|
|
return launchConfig;
|
|
};
|
|
window.NativeBridge.getLaunchPayload = function(callback) {
|
|
invokeCallback(callback, normalizedPayload);
|
|
return normalizedPayload;
|
|
};
|
|
window.getConfig = function(callback) {
|
|
return window.NativeBridge.getConfig(callback);
|
|
};
|
|
window.getConfig.postMessage = function(callback) {
|
|
return window.NativeBridge.getConfig(callback);
|
|
};
|
|
if (payloadChanged) {
|
|
dispatchConfigReady(normalizedPayload);
|
|
postDebug('config.ready', {
|
|
provider: normalizedPayload.provider || '',
|
|
gameId: normalizedPayload.gameId || '',
|
|
roomId: launchConfig.roomId || '',
|
|
code: launchConfig.code
|
|
? String(launchConfig.code).slice(0, 6) + '***'
|
|
: '',
|
|
codeLength: launchConfig.code ? String(launchConfig.code).length : 0,
|
|
entryUrl: entry.entryUrl || ''
|
|
});
|
|
}
|
|
}
|
|
|
|
if (!window.__leaderBridgeReady) {
|
|
window.__leaderBridgeReady = true;
|
|
|
|
function bindCallable(name) {
|
|
const callable = function(payload) {
|
|
return postMessage(name, payload);
|
|
};
|
|
callable.postMessage = function(payload) {
|
|
return callable(payload);
|
|
};
|
|
window[name] = callable;
|
|
return callable;
|
|
}
|
|
|
|
const pay = bindCallable('${LeaderBridgeActions.pay}');
|
|
const closeGame = bindCallable('${LeaderBridgeActions.closeGame}');
|
|
const loadComplete = bindCallable('${LeaderBridgeActions.loadComplete}');
|
|
|
|
window.NativeBridge = window.NativeBridge || {};
|
|
window.NativeBridge.pay = pay;
|
|
window.NativeBridge.closeGame = closeGame;
|
|
window.NativeBridge.loadComplete = loadComplete;
|
|
|
|
window.__leaderBridgeDebug = postDebug;
|
|
postDebug('bridge.ready', {
|
|
href: window.location && window.location.href,
|
|
hasUpdateCoin: typeof window.updateCoin === 'function'
|
|
});
|
|
}
|
|
|
|
applyLaunchPayload(launchPayload);
|
|
})();
|
|
''';
|
|
}
|
|
|
|
static String buildUpdateCoinScript() {
|
|
return '''
|
|
(function() {
|
|
if (typeof window.updateCoin === 'function') {
|
|
window.updateCoin();
|
|
return;
|
|
}
|
|
if (window.NativeBridge && typeof window.NativeBridge.updateCoin === 'function') {
|
|
window.NativeBridge.updateCoin();
|
|
return;
|
|
}
|
|
if (typeof window.onUpdateCoin === 'function') {
|
|
window.onUpdateCoin();
|
|
return;
|
|
}
|
|
$channelName.postMessage(JSON.stringify({
|
|
action: '${LeaderBridgeActions.debugLog}',
|
|
payload: {
|
|
tag: 'game.updateCoin.missing',
|
|
message: 'No updateCoin handler found on game page.'
|
|
}
|
|
}));
|
|
})();
|
|
''';
|
|
}
|
|
}
|