diff --git a/android/src/main/java/com/reactnativecompressor/NitroPromiseAdapter.java b/android/src/main/java/com/reactnativecompressor/NitroPromiseAdapter.java new file mode 100644 index 0000000..8d149f3 --- /dev/null +++ b/android/src/main/java/com/reactnativecompressor/NitroPromiseAdapter.java @@ -0,0 +1,105 @@ +package com.reactnativecompressor; + +import com.facebook.react.bridge.Promise; +import com.facebook.react.bridge.WritableMap; +import java.util.concurrent.atomic.AtomicBoolean; +import kotlin.Unit; +import kotlin.jvm.functions.Function0; +import kotlin.jvm.functions.Function1; + +/** + * Adapts the React Native bridge {@link Promise} to a Nitro + * {@link com.margelo.nitro.core.Promise} so the existing domain methods — which speak the bridge + * {@code Promise} contract ({@code resolve}/{@code reject}) — can drive a Nitro Promise unchanged. + * + *

{@code convert} maps the resolved bridge value to the Nitro result type {@code T}; + * {@code onSettle} runs once on resolve/reject (used to unregister progress callbacks). + * + *

This is intentionally written in Java rather than Kotlin: the bridge {@code Promise} interface + * declares {@code code} as non-null ({@code String}) on some React Native versions and nullable + * ({@code String?}) on others. Kotlin override parameter types are invariant, so a single Kotlin + * source can only match one of those. Java erases nullability for override matching, so one Java + * implementation satisfies every React Native version. + */ +public class NitroPromiseAdapter implements Promise { + private final com.margelo.nitro.core.Promise promise; + private final Function1 convert; + private final Function0 onSettle; + private final AtomicBoolean settled = new AtomicBoolean(false); + + public NitroPromiseAdapter( + com.margelo.nitro.core.Promise promise, + Function1 convert, + Function0 onSettle) { + this.promise = promise; + this.convert = convert; + this.onSettle = onSettle; + } + + @Override + public void resolve(Object value) { + if (!settled.compareAndSet(false, true)) return; + onSettle.invoke(); + promise.resolve(convert.invoke(value)); + } + + private void rejectInternal(String message, Throwable throwable) { + if (!settled.compareAndSet(false, true)) return; + onSettle.invoke(); + promise.reject( + throwable != null + ? throwable + : new Throwable(message != null ? message : "react-native-compressor error")); + } + + @Override + public void reject(String code, String message) { + rejectInternal(message != null ? message : code, null); + } + + @Override + public void reject(String code, Throwable throwable) { + rejectInternal(code, throwable); + } + + @Override + public void reject(String code, String message, Throwable throwable) { + rejectInternal(message != null ? message : code, throwable); + } + + @Override + public void reject(Throwable throwable) { + rejectInternal(throwable.getMessage(), throwable); + } + + @Override + public void reject(Throwable throwable, WritableMap userInfo) { + rejectInternal(throwable.getMessage(), throwable); + } + + @Override + public void reject(String code, WritableMap userInfo) { + rejectInternal(code, null); + } + + @Override + public void reject(String code, Throwable throwable, WritableMap userInfo) { + rejectInternal(code, throwable); + } + + @Override + public void reject(String code, String message, WritableMap userInfo) { + rejectInternal(message != null ? message : code, null); + } + + @Override + public void reject(String code, String message, Throwable throwable, WritableMap userInfo) { + rejectInternal(message != null ? message : code, throwable); + } + + @Deprecated + @Override + public void reject(String message) { + rejectInternal(message, null); + } +} diff --git a/android/src/main/java/com/reactnativecompressor/NitroPromiseAdapter.kt b/android/src/main/java/com/reactnativecompressor/NitroPromiseAdapter.kt deleted file mode 100644 index a6c142a..0000000 --- a/android/src/main/java/com/reactnativecompressor/NitroPromiseAdapter.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.reactnativecompressor - -import com.facebook.react.bridge.Promise -import com.facebook.react.bridge.WritableMap -import java.util.concurrent.atomic.AtomicBoolean -import com.margelo.nitro.core.Promise as NitroPromise - -/** - * Adapts the React Native bridge [Promise] to a Nitro [NitroPromise] so the existing - * domain methods — which speak the bridge `Promise` contract (`resolve`/`reject`) — can - * drive a Nitro Promise unchanged. - * - * [convert] maps the resolved bridge value to the Nitro result type [T]; - * [onSettle] runs once on resolve/reject (used to unregister progress callbacks). - */ -class NitroPromiseAdapter( - private val promise: NitroPromise, - private val convert: (Any?) -> T, - private val onSettle: () -> Unit = {}, -) : Promise { - private val settled = AtomicBoolean(false) - - override fun resolve(value: Any?) { - if (!settled.compareAndSet(false, true)) return - onSettle() - promise.resolve(convert(value)) - } - - private fun rejectInternal(message: String?, throwable: Throwable?) { - if (!settled.compareAndSet(false, true)) return - onSettle() - promise.reject(throwable ?: Throwable(message ?: "react-native-compressor error")) - } - - override fun reject(code: String?, message: String?) = rejectInternal(message ?: code, null) - - override fun reject(code: String?, throwable: Throwable?) = rejectInternal(code, throwable) - - override fun reject(code: String?, message: String?, throwable: Throwable?) = rejectInternal(message ?: code, throwable) - - override fun reject(throwable: Throwable) = rejectInternal(throwable.message, throwable) - - override fun reject(throwable: Throwable, userInfo: WritableMap) = rejectInternal(throwable.message, throwable) - - override fun reject(code: String?, userInfo: WritableMap) = rejectInternal(code, null) - - override fun reject(code: String?, throwable: Throwable?, userInfo: WritableMap) = rejectInternal(code, throwable) - - override fun reject(code: String?, message: String?, userInfo: WritableMap) = rejectInternal(message ?: code, null) - - override fun reject(code: String?, message: String?, throwable: Throwable?, userInfo: WritableMap?) = rejectInternal(message ?: code, throwable) - - @Deprecated("Prefer passing a module-specific error code to JS.", ReplaceWith("reject(code, message)")) - override fun reject(message: String) = rejectInternal(message, null) -}