theta-client-react-native
Version:
This library provides a way to control RICOH THETA using.
248 lines (216 loc) • 8.37 kB
text/typescript
import type { TurboModule } from 'react-native';
import { TurboModuleRegistry } from 'react-native';
import { normalizeNativeError } from './theta-repository/theta-web-api-error';
import { isPromiseLike } from './theta-repository/libs';
export interface Spec extends TurboModule {
// Event listeners for Android old architecture only
addListener(eventName: string): void;
removeListeners(count: number): void;
// API Log
setApiLogListener(enabled: boolean): Promise<void>;
// Initialize
initialize(
endPoint: string,
config: Object | null,
timeout: Object | null
): Promise<boolean>;
isInitialized(): Promise<boolean>;
// Get theta info
getThetaModel(): Promise<string | undefined>;
getThetaInfo(): Promise<Object>;
getThetaLicense(): Promise<string>;
getThetaState(): Promise<Object>;
// File operations
listFiles(
fileType: string,
startPosition: number,
entryCount: number,
storage: string | null
): Promise<Object>;
deleteFiles(fileUrls: Array<string>): Promise<boolean>;
deleteAllFiles(): Promise<boolean>;
deleteAllImageFiles(): Promise<boolean>;
deleteAllVideoFiles(): Promise<boolean>;
// Options
getOptions(optionNames: Array<string>): Promise<Object>;
setOptions(options: Object): Promise<boolean>;
// Live preview
getLivePreview(): Promise<boolean>;
stopLivePreview(): Promise<boolean>;
// Photo capture
getPhotoCaptureBuilder(): Promise<void>;
buildPhotoCapture(options: Object): Promise<void>;
takePicture(): Promise<string>;
// TimeShift capture
getTimeShiftCaptureBuilder(): Promise<void>;
buildTimeShiftCapture(options: Object): Promise<void>;
startTimeShiftCapture(): Promise<string>;
cancelTimeShiftCapture(): Promise<void>;
// TimeShift manual capture
getTimeShiftManualCaptureBuilder(): Promise<void>;
buildTimeShiftManualCapture(options: Object): Promise<void>;
startTimeShiftManualCapture(): Promise<string>;
startTimeShiftManualSecondCapture(): Promise<string>;
cancelTimeShiftManualCapture(): Promise<void>;
// Video capture
getVideoCaptureBuilder(): Promise<void>;
buildVideoCapture(options: Object): Promise<void>;
startVideoCapture(): Promise<string>;
stopVideoCapture(): Promise<string>;
// Limitless interval capture
getLimitlessIntervalCaptureBuilder(): Promise<void>;
buildLimitlessIntervalCapture(options: Object): Promise<void>;
startLimitlessIntervalCapture(): Promise<Array<string>>;
stopLimitlessIntervalCapture(): Promise<Array<string>>;
// Shot count specified interval capture
getShotCountSpecifiedIntervalCaptureBuilder(shotCount: number): Promise<void>;
buildShotCountSpecifiedIntervalCapture(options: Object): Promise<void>;
startShotCountSpecifiedIntervalCapture(): Promise<Array<string>>;
cancelShotCountSpecifiedIntervalCapture(): Promise<void>;
// Composite interval capture
getCompositeIntervalCaptureBuilder(shootingTimeSec: number): Promise<void>;
buildCompositeIntervalCapture(options: Object): Promise<void>;
startCompositeIntervalCapture(): Promise<Array<string>>;
cancelCompositeIntervalCapture(): Promise<void>;
// Burst capture
getBurstCaptureBuilder(
burstCaptureNum: string,
burstBracketStep: string,
burstCompensation: string,
burstMaxExposureTime: string,
burstEnableIsoControl: string,
burstOrder: string
): Promise<void>;
buildBurstCapture(options: Object): Promise<void>;
startBurstCapture(): Promise<Array<string>>;
cancelBurstCapture(): Promise<void>;
// Multi bracket capture
getMultiBracketCaptureBuilder(): Promise<void>;
buildMultiBracketCapture(options: Object): Promise<void>;
startMultiBracketCapture(): Promise<Array<string>>;
cancelMultiBracketCapture(): Promise<void>;
// Continuous capture
getContinuousCaptureBuilder(): Promise<void>;
buildContinuousCapture(options: Object): Promise<void>;
startContinuousCapture(): Promise<Array<string>>;
// Metadata
getMetadata(fileUrl: string): Promise<Object>;
// Commands
reboot(): Promise<void>;
reset(): Promise<boolean>;
restoreSettings(): Promise<boolean>;
stopSelfTimer(): Promise<boolean>;
// Video convert
convertVideoFormats(
fileUrl: string,
toLowResolution: boolean,
applyTopBottomCorrection: boolean
): Promise<string>;
cancelVideoConvert(): Promise<boolean>;
// WLAN
finishWlan(): Promise<boolean>;
listAccessPoints(): Promise<Array<Object>>;
setAccessPointDynamically(params: Object): Promise<boolean>;
setAccessPointStatically(params: Object): Promise<boolean>;
setAccessPointConnectionPriority(
ssid: string,
connectionPriority: number,
ssidStealth: boolean
): Promise<boolean>;
deleteAccessPoint(ssid: string): Promise<boolean>;
// My setting
getMySetting(captureMode: string): Promise<Object>;
getMySettingFromOldModel(optionNames: Array<string>): Promise<Object>;
setMySetting(captureMode: string, options: Object): Promise<boolean>;
deleteMySetting(captureMode: string): Promise<boolean>;
// Plugin
listPlugins(): Promise<Array<Object>>;
setPlugin(packageName: string): Promise<boolean>;
startPlugin(packageName: string): Promise<boolean>;
stopPlugin(): Promise<boolean>;
getPluginLicense(packageName: string): Promise<string>;
getPluginOrders(): Promise<Array<string>>;
setPluginOrders(plugins: Array<string>): Promise<boolean>;
// Bluetooth
setBluetoothDevice(uuid: string): Promise<string>;
// Event WebSocket
getEventWebSocket(): Promise<void>;
eventWebSocketStart(): Promise<void>;
eventWebSocketStop(): Promise<void>;
}
// New Architecture only: resolve TurboModule on first access
let cachedModule: Spec | null = null;
function getModule(): Spec {
if (cachedModule != null) {
return cachedModule;
}
const turbo = TurboModuleRegistry.get<Spec>('ThetaClientReactNative');
if (turbo == null) {
// In test environment, return a mock object instead of throwing
if (
process.env.NODE_ENV === 'test' ||
process.env.JEST_WORKER_ID !== undefined
) {
cachedModule = {} as Spec;
return cachedModule;
}
throw new Error(
'ThetaClientReactNative TurboModule could not be found. Ensure New Architecture is enabled and the module is registered in the native binary.'
);
}
cachedModule = turbo;
return cachedModule;
}
export default new Proxy({} as Spec, {
get(_, prop: string | symbol) {
const module = getModule();
const value = module[prop as keyof Spec];
if (typeof value !== 'function') {
return value;
}
// Wrap native async methods so rejected promises carry parsed THETA Web API details,
// without adding extra microtask ticks on the success path.
return (...args: unknown[]) => {
const result = (value as (...innerArgs: unknown[]) => unknown).apply(
module,
args
);
if (!isPromiseLike(result)) {
return result;
}
// Wrap the original promise in a Proxy that intercepts .then()/.catch() to
// normalize errors, while preserving the microtask timing of the original
// promise on the success path (no extra .then() hops added).
return new Proxy(result, {
get(target, p) {
if (p === 'then') {
return (
onFulfilled: ((value: unknown) => unknown) | null | undefined,
onRejected: ((reason: unknown) => unknown) | null | undefined
) => {
const normalizedRejected = (err: unknown): unknown =>
onRejected != null
? onRejected(normalizeNativeError(err))
: Promise.reject(normalizeNativeError(err));
return (target as PromiseLike<unknown>).then(
onFulfilled ?? undefined,
normalizedRejected
);
};
}
if (p === 'catch') {
return (
onRejected: ((reason: unknown) => unknown) | null | undefined
) =>
(target as Promise<unknown>).catch((err: unknown) =>
onRejected != null
? onRejected(normalizeNativeError(err))
: Promise.reject(normalizeNativeError(err))
);
}
return (target as unknown as Record<string | symbol, unknown>)[p];
},
});
};
},
});