@circle-apps/sdk
Version:
Official SDK for Celia Mini Apps integration
353 lines (351 loc) • 14.5 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.CeliaSDK = void 0;
class CeliaSDK {
constructor() {
var _a, _b;
this.isInitialized = false;
if (typeof window === "undefined") {
throw new Error("CeliaSDK can only be used in a browser environment");
}
// Check both WebView and user agent
this.isWebView = !!(window.ReactNativeWebView ||
((_b = (_a = window.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.reactNativeWebView));
this.isCircleMiniApp = true;
if (!this.isWebView) {
console.warn("CeliaSDK is not running in a WebView. Some features may not be available.");
return;
}
this.initialize().catch((error) => {
console.error("Failed to initialize CeliaSDK:", error);
// Error is already logged, no need to emit an event
});
}
verifyEnvironment() {
var _a, _b;
const isWebView = !!(window.ReactNativeWebView ||
((_b = (_a = window.webkit) === null || _a === void 0 ? void 0 : _a.messageHandlers) === null || _b === void 0 ? void 0 : _b.reactNativeWebView));
return {
isWebView,
isCircleMiniApp: true,
isValid: isWebView,
};
}
initialize() {
return __awaiter(this, void 0, void 0, function* () {
if (this.isInitialized)
return;
try {
const { isValid } = this.verifyEnvironment();
if (!isValid) {
throw new Error(`Invalid environment. CeliaSDK must run in a WebView`);
}
yield this.injectBridge();
this.setupMessageListener();
this.isInitialized = true;
}
catch (error) {
console.error("Failed to initialize CeliaSDK:", error);
throw error;
}
});
}
authenticate(options) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
try {
if (!options || !options.appID) {
throw new Error("App ID is required for authentication");
}
const callbackId = Date.now().toString();
this.setupCallback(callbackId, resolve, reject);
this.postMessage({
type: "AUTHENTICATE",
payload: {
appID: options.appID,
callbackId,
},
});
}
catch (error) {
reject(error);
}
});
});
}
share(options) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
try {
const callbackId = Date.now().toString();
this.setupCallback(callbackId, resolve, reject);
this.postMessage({
type: "SHARE",
payload: Object.assign(Object.assign({}, options), { callbackId }),
});
}
catch (error) {
reject(error);
}
});
});
}
showAd(options) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
try {
if (!options || !options.type) {
throw new Error("Ad type is required");
}
// For banner ads, redirect to showBannerAd method
if (options.type === 'banner') {
if (!options.containerId) {
throw new Error("Container ID is required for banner ads");
}
return this.showBannerAd({
containerId: options.containerId,
size: options.size,
unitId: options.adUnitId,
requestOptions: options.requestOptions,
}).then(resolve).catch(reject);
}
const callbackId = Date.now().toString();
this.setupCallback(callbackId, resolve, reject);
this.postMessage({
type: "SHOW_AD",
payload: Object.assign(Object.assign({ adType: options.type }, options), { callbackId }),
});
}
catch (error) {
reject(error);
}
});
});
}
showBannerAd(options) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
try {
if (!options || !options.containerId) {
throw new Error("Container ID is required for banner ads");
}
const callbackId = Date.now().toString();
this.setupCallback(callbackId, resolve, reject);
this.postMessage({
type: "SHOW_BANNER_AD",
payload: {
containerId: options.containerId,
size: options.size || 'ANCHORED_ADAPTIVE_BANNER',
unitId: options.unitId,
requestOptions: options.requestOptions,
callbackId,
},
});
}
catch (error) {
reject(error);
}
});
});
}
hideBannerAd(options) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
try {
if (!options || !options.containerId) {
throw new Error("Container ID is required to hide banner ad");
}
const callbackId = Date.now().toString();
this.setupCallback(callbackId, resolve, reject);
this.postMessage({
type: "HIDE_BANNER_AD",
payload: {
containerId: options.containerId,
callbackId,
},
});
}
catch (error) {
reject(error);
}
});
});
}
getLanguage() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
try {
// First check if language has been eagerly injected
if (typeof window !== "undefined") {
// Check for cached language in window object
if (window._celiaAppLanguage) {
return resolve({ language: window._celiaAppLanguage });
}
// Check for cached language in celiaMini object
if (window.celiaMini && window.celiaMini._cachedLanguage) {
return resolve({ language: window.celiaMini._cachedLanguage });
}
// Set up a one-time event listener for language ready event
const languageReadyHandler = (event) => {
window.removeEventListener("celiaLanguageReady", languageReadyHandler);
return resolve(event.detail);
};
window.addEventListener("celiaLanguageReady", languageReadyHandler);
}
// If no cached language found, request it from native side
const callbackId = Date.now().toString();
this.setupCallback(callbackId, resolve, reject);
this.postMessage({
type: "GET_LANGUAGE",
payload: {
callbackId,
},
});
}
catch (error) {
reject(error);
}
});
});
}
setupCallback(callbackId, resolve, reject) {
if (!window._celiaMiniCallbacks) {
window._celiaMiniCallbacks = {};
}
window._celiaMiniCallbacks[callbackId] = {
resolve,
reject,
timeout: setTimeout(() => {
var _a;
reject(new Error("Operation timed out"));
(_a = window._celiaMiniCallbacks) === null || _a === void 0 ? true : delete _a[callbackId];
}, 30000), // 30 second timeout
};
}
postMessage(message) {
if (!window.ReactNativeWebView) {
throw new Error("ReactNativeWebView is not available");
}
window.ReactNativeWebView.postMessage(JSON.stringify({
type: "FROM_WEBVIEW",
payload: message,
}));
}
injectBridge() {
return new Promise((resolve) => {
if (typeof window === "undefined") {
resolve();
return;
}
// If bridge already exists, resolve immediately
if (window.ReactNativeWebView) {
resolve();
return;
}
// Create a script to inject the bridge
const script = document.createElement("script");
script.text = `
(function() {
if (!window.ReactNativeWebView) {
window.ReactNativeWebView = {
postMessage: function(message) {
try {
const payload = typeof message === 'string' ? message : JSON.stringify(message);
window.postMessage(JSON.stringify({
type: 'FROM_WEBVIEW',
payload: typeof message === 'string' ? JSON.parse(message) : message
}), '*');
return true;
} catch (e) {
console.error('Failed to post message:', e);
return false;
}
}
};
}
// Notify that the bridge is ready
if (window.dispatchEvent) {
window.dispatchEvent(new Event('celia:bridgeReady'));
}
})();
`;
const onBridgeReady = () => {
document.removeEventListener("celia:bridgeReady", onBridgeReady);
resolve();
};
// Set a timeout in case the bridge never initializes
const timeoutId = setTimeout(() => {
document.removeEventListener("celia:bridgeReady", onBridgeReady);
console.warn("Bridge initialization timed out");
resolve(); // Resolve anyway to prevent hanging
}, 2000); // 2 second timeout
document.addEventListener("celia:bridgeReady", onBridgeReady, {
once: true,
});
document.head.appendChild(script);
});
}
setupMessageListener() {
if (typeof window === "undefined") {
// Return a no-op cleanup function
return () => { };
}
const handleMessage = (event) => {
var _a;
try {
// Handle both direct messages and wrapped messages
let message;
try {
message =
typeof event.data === "string"
? JSON.parse(event.data)
: event.data;
}
catch (e) {
console.warn("Failed to parse message:", event.data);
return;
}
// Check if this is a wrapped message
const payload = message.type === "FROM_WEBVIEW" ? message.payload : message;
if (!payload || !payload.type)
return;
// Handle callback responses
if (payload.callbackId &&
((_a = window._celiaMiniCallbacks) === null || _a === void 0 ? void 0 : _a[payload.callbackId])) {
const { resolve, reject } = window._celiaMiniCallbacks[payload.callbackId];
clearTimeout(window._celiaMiniCallbacks[payload.callbackId].timeout);
if (payload.error) {
reject(new Error(payload.error));
}
else {
resolve(payload);
}
delete window._celiaMiniCallbacks[payload.callbackId];
}
// Events are now handled through callbacks
}
catch (error) {
console.error("Error handling message:", error, event.data);
}
};
window.addEventListener("message", handleMessage);
return () => {
window.removeEventListener("message", handleMessage);
};
}
handleIncomingEvent(_event, _payload) {
// Events are now handled through callbacks
}
}
exports.CeliaSDK = CeliaSDK;
// Export the class
exports.default = CeliaSDK;