import type { WidgetCallbacks, WidgetData, WidgetOptions, WidgetSamplerLog, WidgetType } from "./domain";
import { normalizeWidgetOptions } from "./domain/WidgetOptions";
import { AsyncStorageService } from "./services/storage/AsyncStorageService";
import { buildStorageId } from "./services/storage/StorageIdBuilder";
import { WidgetStateManager, type TimestampField } from "./services/WidgetStateManager";
export interface WidgetConfig {
instanceKey: string;
type: WidgetType;
data: WidgetData;
options?: WidgetOptions;
callbacks: WidgetCallbacks;
}
type ShowHandler = (config: WidgetConfig) => void;
type DismissHandler = () => void;
let registeredShowHandler: ShowHandler | null = null;
let registeredDismissHandler: DismissHandler | null = null;
export function _registerHost(show: ShowHandler, dismiss: DismissHandler) {
registeredShowHandler = show;
registeredDismissHandler = dismiss;
}
export function _unregisterHost() {
registeredShowHandler = null;
registeredDismissHandler = null;
}
export class SoluCXWidget {
private _instanceKey: string = "";
private _type: WidgetType = "bottom";
private _data: WidgetData = {};
private _options: WidgetOptions | null = {};
private _callbacks: WidgetCallbacks = {};
private constructor(instanceKey: string) {
this._instanceKey = instanceKey;
}
// --- Builder methods ---
static create(instanceKey: string): SoluCXWidget {
return new SoluCXWidget(instanceKey);
}
setData(data: WidgetData): this {
this._data = { ...data };
return this;
}
setOptions(options: WidgetOptions): this {
const isObjectValid = !!options && Object.keys(options).length > 0;
this._options = isObjectValid ? normalizeWidgetOptions(options) : null;
return this;
}
setCallbacks(callbacks: WidgetCallbacks): this {
this._callbacks = { ...callbacks };
return this;
}
setType(type: WidgetType): this {
this._type = type;
return this;
}
show(): void {
if (!registeredShowHandler) {
throw new Error("[SoluCXWidget] Cannot show widget — no is mounted. " + "Add at the root of your app.");
}
registeredShowHandler({
instanceKey: this._instanceKey,
type: this._type,
data: { ...this._data },
options: { ...this._options },
callbacks: { ...this._callbacks },
});
this._cleanup();
}
// --- Static: dismiss ---
static dismiss(): void {
registeredDismissHandler?.();
}
// --- Log management (instance methods) ---
/**
* Build the storage ID from the builder state.
* Uses instanceKey + journey (from data) + userId (from data).
*/
private _buildStorageId(): string {
return buildStorageId(this._instanceKey, this._data);
}
private _createStateManager(): WidgetStateManager {
const storageId = this._buildStorageId();
const storageService = new AsyncStorageService(storageId);
return new WidgetStateManager(storageService, storageId);
}
/**
* Override a specific event timestamp.
* Uses instanceKey from create() and journey/userId from setData().
*
* @param field - The timestamp field to override (e.g., 'lastFirstAccess', 'lastDismiss')
* @param date - The date to set (Date object or timestamp in milliseconds). Use 0 to clear.
*/
async overrideTimestamp(field: TimestampField, date: Date | number): Promise {
await this._createStateManager().overrideTimestamp(field, date);
return this;
}
/**
* Get the current widget logs.
* Uses instanceKey from create() and journey/userId from setData().
*/
async getWidgetLogs(): Promise {
return this._createStateManager().getLogs();
}
/**
* Clear all widget logs.
* Uses instanceKey from create() and journey/userId from setData().
*/
async clearWidgetLogs(): Promise {
await this._createStateManager().clearLogs();
return this;
}
// --- Private ---
private _cleanup(): void {
this._type = "bottom";
this._data = {};
this._options = {};
this._callbacks = {};
}
}