mirror of
https://github.com/aljazceru/react-native-pubky.git
synced 2025-12-18 15:14:20 +01:00
Merge pull request #16 from pubky/add-delete-session
feat: add delete_file & session
This commit is contained in:
35
README.md
35
README.md
@@ -18,19 +18,20 @@ npm install @synonymdev/react-native-pubky
|
||||
- [x] [resolveHttps](#resolveHttps): Resolve HTTPS records.
|
||||
- [x] [signUp](#signUp): Sign-up to a homeserver and update Pkarr accordingly.
|
||||
- [x] [signIn](#signIn): Sign-in to a homeserver.
|
||||
- [x] [session](#session): Check the current session for a given Pubky in its homeserver.
|
||||
- [x] [signOut](#signOut): Sign-out from a homeserver.
|
||||
- [x] [put](#put): Upload a small payload to a given path.
|
||||
- [x] [get](#get): Download a small payload from a given path relative to a pubky author.
|
||||
- [x] [list](#list): Returns a list of Pubky URLs of the files in the path of the `url` provided.
|
||||
- [x] [delete](#delete): Delete a file at a path relative to a pubky author.
|
||||
- [x] [generateSecretKey](#generateSecretKey): Generate a secret key.
|
||||
- [x] [getPublicKeyFromSecretKey](#getPublicKeyFromSecretKey): Get the public key string and uri from a secret key.
|
||||
- [x] [create_recovery_file](#createRecoveryFile): Create a recovery file.
|
||||
- [x] [decrypt_recovery_file](#decryptRecoveryFile): Decrypt a recovery file.
|
||||
|
||||
### Methods to be Implemented
|
||||
- [ ] session: Check the current session for a given Pubky in its homeserver.
|
||||
- [ ] delete: Delete a file at a path relative to a pubky author.
|
||||
|
||||
- [ ] setProfile: Set profile information for a pubky.
|
||||
- [ ] getProfile: Get profile information for a pubky.
|
||||
|
||||
## Usage
|
||||
### <a name="auth"></a>Auth
|
||||
@@ -161,6 +162,20 @@ if (listRes.isErr()) {
|
||||
console.log(listRes.value);
|
||||
```
|
||||
|
||||
### <a name="deleteFile"></a>deleteFile
|
||||
```js
|
||||
import { deleteFile } from '@synonymdev/react-native-pubky';
|
||||
|
||||
const deleteFileRes = await deleteFile(
|
||||
'pubky://z4e8s17cou9qmuwen8p1556jzhf1wktmzo6ijsfnri9c4hnrdfty/pub/' // URL
|
||||
);
|
||||
if (deleteFileRes.isErr()) {
|
||||
console.log(deleteFileRes.error.message);
|
||||
return;
|
||||
}
|
||||
console.log(deleteFileRes.value);
|
||||
```
|
||||
|
||||
### <a name="generateSecretKey"></a>generateSecretKey
|
||||
```js
|
||||
import { generateSecretKey } from '@synonymdev/react-native-pubky';
|
||||
@@ -214,6 +229,20 @@ if (signInRes.isErr()) {
|
||||
console.log(signInRes.value);
|
||||
```
|
||||
|
||||
### <a name="session"></a>sessionRes
|
||||
```js
|
||||
import { session } from '@synonymdev/react-native-pubky';
|
||||
|
||||
const sessionRes = await session(
|
||||
'z4e8s17cou9qmuwen8p1556jzhf1wktmzo6ijsfnri9c4hnrdfty' // Public Key
|
||||
);
|
||||
if (sessionRes.isErr()) {
|
||||
console.log(sessionRes.error.message);
|
||||
return;
|
||||
}
|
||||
console.log(sessionRes.value);
|
||||
```
|
||||
|
||||
### <a name="signOut"></a>signIn
|
||||
```js
|
||||
import { signOut } from '@synonymdev/react-native-pubky';
|
||||
|
||||
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReactApplicationContext
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
||||
import com.facebook.react.bridge.ReactMethod
|
||||
import com.facebook.react.bridge.Promise
|
||||
import com.facebook.react.modules.core.DeviceEventManagerModule
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -12,299 +13,389 @@ import kotlinx.coroutines.withContext
|
||||
import uniffi.pubkymobile.*
|
||||
|
||||
class PubkyModule(reactContext: ReactApplicationContext) :
|
||||
ReactContextBaseJavaModule(reactContext) {
|
||||
ReactContextBaseJavaModule(reactContext) {
|
||||
|
||||
override fun getName(): String {
|
||||
return NAME
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun auth(url: String, secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = auth(url, secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun parseAuthUrl(url: String, promise: Promise) {
|
||||
try {
|
||||
val result = parseAuthUrl(url)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
promise.resolve(array)
|
||||
} catch (e: Exception) {
|
||||
promise.reject("Error", e.message)
|
||||
override fun getName(): String {
|
||||
return NAME
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun publish(recordName: String, recordContent: String, secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = publish(recordName, recordContent, secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun resolve(publicKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = resolve(publicKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun signUp(secretKey: String, homeserver: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = signUp(secretKey, homeserver)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun signIn(secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = signIn(secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun signOut(secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = signOut(secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun put(url: String, content: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = put(url, content)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun get(url: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = get(url)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun publishHttps(recordName: String, target: String, secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = publishHttps(recordName, target, secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun resolveHttps(publicKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = resolveHttps(publicKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun list(url: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = list(url)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun generateSecretKey(promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = generate_secret_key()
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun getPublicKeyFromSecretKey(secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = getPublicKeyFromSecretKey(secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun createRecoveryFile(secretKey: String, passphrase: String, promise: Promise) {
|
||||
try {
|
||||
val result = createRecoveryFile(secretKey, passphrase)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
private val eventListener = object : EventListener {
|
||||
override fun onEventOccurred(eventData: String) {
|
||||
reactContext
|
||||
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
||||
.emit("PubkyEvent", eventData)
|
||||
}
|
||||
promise.resolve(array)
|
||||
} catch (e: Exception) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun decryptRecoveryFile(recoveryFile: String, passphrase: String, promise: Promise) {
|
||||
try {
|
||||
val result = decryptRecoveryFile(recoveryFile, passphrase)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
@ReactMethod
|
||||
fun setEventListener(promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
setEventListener(eventListener)
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
promise.resolve(array)
|
||||
} catch (e: Exception) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun removeEventListener(promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
removeEventListener()
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(null)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun deleteFile(url: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = deleteFile(url)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun session(pubky: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = session(pubky)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun auth(url: String, secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = auth(url, secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun parseAuthUrl(url: String, promise: Promise) {
|
||||
try {
|
||||
val result = parseAuthUrl(url)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
promise.resolve(array)
|
||||
} catch (e: Exception) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun publish(recordName: String, recordContent: String, secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = publish(recordName, recordContent, secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun resolve(publicKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = resolve(publicKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun signUp(secretKey: String, homeserver: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = signUp(secretKey, homeserver)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun signIn(secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = signIn(secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun signOut(secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = signOut(secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun put(url: String, content: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = put(url, content)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun get(url: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = get(url)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun publishHttps(recordName: String, target: String, secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = publishHttps(recordName, target, secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun resolveHttps(publicKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = resolveHttps(publicKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun list(url: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = list(url)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun generateSecretKey(promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = generateSecretKey()
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun getPublicKeyFromSecretKey(secretKey: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = getPublicKeyFromSecretKey(secretKey)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun createRecoveryFile(secretKey: String, passphrase: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = createRecoveryFile(secretKey, passphrase)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
fun decryptRecoveryFile(recoveryFile: String, passphrase: String, promise: Promise) {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
try {
|
||||
val result = decryptRecoveryFile(recoveryFile, passphrase)
|
||||
val array = Arguments.createArray().apply {
|
||||
result.forEach { pushString(it) }
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.resolve(array)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
withContext(Dispatchers.Main) {
|
||||
promise.reject("Error", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME = "Pubky"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val NAME = "Pubky"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,9 +29,10 @@ import java.nio.ByteOrder
|
||||
import java.nio.CharBuffer
|
||||
import java.nio.charset.CodingErrorAction
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.coroutines.resume
|
||||
import kotlinx.coroutines.CancellableContinuation
|
||||
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
// This is a helper for safely working with byte buffers returned from the Rust code.
|
||||
// A rust-owned buffer is represented by its capacity, its current length, and a
|
||||
@@ -380,15 +381,29 @@ internal interface _UniFFILib : Library {
|
||||
.also { lib: _UniFFILib ->
|
||||
uniffiCheckContractApiVersion(lib)
|
||||
uniffiCheckApiChecksums(lib)
|
||||
uniffiRustFutureContinuationCallback.register(lib)
|
||||
FfiConverterTypeEventListener.register(lib)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun uniffi_pubkymobile_fn_free_eventnotifier(`ptr`: Pointer,_uniffi_out_err: RustCallStatus,
|
||||
): Unit
|
||||
fun uniffi_pubkymobile_fn_init_callback_eventlistener(`callbackStub`: ForeignCallback,_uniffi_out_err: RustCallStatus,
|
||||
): Unit
|
||||
fun uniffi_pubkymobile_fn_func_auth(`url`: RustBuffer.ByValue,`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_get(`url`: RustBuffer.ByValue,
|
||||
): Pointer
|
||||
fun uniffi_pubkymobile_fn_func_create_recovery_file(`secretKey`: RustBuffer.ByValue,`passphrase`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_decrypt_recovery_file(`recoveryFile`: RustBuffer.ByValue,`passphrase`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_delete_file(`url`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_generate_secret_key(_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_get(`url`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_get_public_key_from_secret_key(`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_list(`url`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_parse_auth_url(`url`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
@@ -397,18 +412,24 @@ internal interface _UniFFILib : Library {
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_publish_https(`recordName`: RustBuffer.ByValue,`target`: RustBuffer.ByValue,`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_put(`url`: RustBuffer.ByValue,`content`: RustBuffer.ByValue,
|
||||
): Pointer
|
||||
fun uniffi_pubkymobile_fn_func_put(`url`: RustBuffer.ByValue,`content`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_remove_event_listener(_uniffi_out_err: RustCallStatus,
|
||||
): Unit
|
||||
fun uniffi_pubkymobile_fn_func_resolve(`publicKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_resolve_https(`publicKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_sign_in(`secretKey`: RustBuffer.ByValue,
|
||||
): Pointer
|
||||
fun uniffi_pubkymobile_fn_func_sign_out(`secretKey`: RustBuffer.ByValue,
|
||||
): Pointer
|
||||
fun uniffi_pubkymobile_fn_func_sign_up(`secretKey`: RustBuffer.ByValue,`homeserver`: RustBuffer.ByValue,
|
||||
): Pointer
|
||||
fun uniffi_pubkymobile_fn_func_session(`pubky`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_set_event_listener(`listener`: Long,_uniffi_out_err: RustCallStatus,
|
||||
): Unit
|
||||
fun uniffi_pubkymobile_fn_func_sign_in(`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_sign_out(`secretKey`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun uniffi_pubkymobile_fn_func_sign_up(`secretKey`: RustBuffer.ByValue,`homeserver`: RustBuffer.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun ffi_pubkymobile_rustbuffer_alloc(`size`: Int,_uniffi_out_err: RustCallStatus,
|
||||
): RustBuffer.ByValue
|
||||
fun ffi_pubkymobile_rustbuffer_from_bytes(`bytes`: ForeignBytes.ByValue,_uniffi_out_err: RustCallStatus,
|
||||
@@ -525,8 +546,18 @@ internal interface _UniFFILib : Library {
|
||||
): Unit
|
||||
fun uniffi_pubkymobile_checksum_func_auth(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_create_recovery_file(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_decrypt_recovery_file(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_delete_file(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_generate_secret_key(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_get(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_get_public_key_from_secret_key(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_list(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_parse_auth_url(
|
||||
@@ -537,16 +568,24 @@ internal interface _UniFFILib : Library {
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_put(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_remove_event_listener(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_resolve(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_resolve_https(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_session(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_set_event_listener(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_sign_in(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_sign_out(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_func_sign_up(
|
||||
): Short
|
||||
fun uniffi_pubkymobile_checksum_method_eventlistener_on_event_occurred(
|
||||
): Short
|
||||
fun ffi_pubkymobile_uniffi_contract_version(
|
||||
): Int
|
||||
|
||||
@@ -567,7 +606,22 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) {
|
||||
if (lib.uniffi_pubkymobile_checksum_func_auth() != 61378.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_get() != 5395.toShort()) {
|
||||
if (lib.uniffi_pubkymobile_checksum_func_create_recovery_file() != 55903.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_decrypt_recovery_file() != 59688.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_delete_file() != 57905.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_generate_secret_key() != 63116.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_get() != 21596.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_get_public_key_from_secret_key() != 23603.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_list() != 8522.toShort()) {
|
||||
@@ -582,7 +636,10 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) {
|
||||
if (lib.uniffi_pubkymobile_checksum_func_publish_https() != 14705.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_put() != 47594.toShort()) {
|
||||
if (lib.uniffi_pubkymobile_checksum_func_put() != 51107.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_remove_event_listener() != 6794.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_resolve() != 18303.toShort()) {
|
||||
@@ -591,62 +648,27 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) {
|
||||
if (lib.uniffi_pubkymobile_checksum_func_resolve_https() != 34593.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_sign_in() != 53969.toShort()) {
|
||||
if (lib.uniffi_pubkymobile_checksum_func_session() != 65177.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_sign_out() != 32961.toShort()) {
|
||||
if (lib.uniffi_pubkymobile_checksum_func_set_event_listener() != 19468.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_sign_up() != 28083.toShort()) {
|
||||
if (lib.uniffi_pubkymobile_checksum_func_sign_in() != 21006.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_sign_out() != 59116.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_func_sign_up() != 58756.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
if (lib.uniffi_pubkymobile_checksum_method_eventlistener_on_event_occurred() != 39865.toShort()) {
|
||||
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
|
||||
}
|
||||
}
|
||||
|
||||
// Async support
|
||||
// Async return type handlers
|
||||
|
||||
internal const val UNIFFI_RUST_FUTURE_POLL_READY = 0.toShort()
|
||||
internal const val UNIFFI_RUST_FUTURE_POLL_MAYBE_READY = 1.toShort()
|
||||
|
||||
internal val uniffiContinuationHandleMap = UniFfiHandleMap<CancellableContinuation<Short>>()
|
||||
|
||||
// FFI type for Rust future continuations
|
||||
internal object uniffiRustFutureContinuationCallback: UniFffiRustFutureContinuationCallbackType {
|
||||
override fun callback(continuationHandle: USize, pollResult: Short) {
|
||||
uniffiContinuationHandleMap.remove(continuationHandle)?.resume(pollResult)
|
||||
}
|
||||
|
||||
internal fun register(lib: _UniFFILib) {
|
||||
lib.ffi_pubkymobile_rust_future_continuation_callback_set(this)
|
||||
}
|
||||
}
|
||||
|
||||
internal suspend fun<T, F, E: Exception> uniffiRustCallAsync(
|
||||
rustFuture: Pointer,
|
||||
pollFunc: (Pointer, USize) -> Unit,
|
||||
completeFunc: (Pointer, RustCallStatus) -> F,
|
||||
freeFunc: (Pointer) -> Unit,
|
||||
liftFunc: (F) -> T,
|
||||
errorHandler: CallStatusErrorHandler<E>
|
||||
): T {
|
||||
try {
|
||||
do {
|
||||
val pollResult = suspendCancellableCoroutine<Short> { continuation ->
|
||||
pollFunc(
|
||||
rustFuture,
|
||||
uniffiContinuationHandleMap.insert(continuation)
|
||||
)
|
||||
}
|
||||
} while (pollResult != UNIFFI_RUST_FUTURE_POLL_READY);
|
||||
|
||||
return liftFunc(
|
||||
rustCallWithError(errorHandler, { status -> completeFunc(rustFuture, status) })
|
||||
)
|
||||
} finally {
|
||||
freeFunc(rustFuture)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Public interface members begin here.
|
||||
|
||||
@@ -706,6 +728,384 @@ public object FfiConverterString: FfiConverter<String, RustBuffer.ByValue> {
|
||||
}
|
||||
|
||||
|
||||
// Interface implemented by anything that can contain an object reference.
|
||||
//
|
||||
// Such types expose a `destroy()` method that must be called to cleanly
|
||||
// dispose of the contained objects. Failure to call this method may result
|
||||
// in memory leaks.
|
||||
//
|
||||
// The easiest way to ensure this method is called is to use the `.use`
|
||||
// helper method to execute a block and destroy the object at the end.
|
||||
interface Disposable {
|
||||
fun destroy()
|
||||
companion object {
|
||||
fun destroy(vararg args: Any?) {
|
||||
args.filterIsInstance<Disposable>()
|
||||
.forEach(Disposable::destroy)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T : Disposable?, R> T.use(block: (T) -> R) =
|
||||
try {
|
||||
block(this)
|
||||
} finally {
|
||||
try {
|
||||
// N.B. our implementation is on the nullable type `Disposable?`.
|
||||
this?.destroy()
|
||||
} catch (e: Throwable) {
|
||||
// swallow
|
||||
}
|
||||
}
|
||||
|
||||
// The base class for all UniFFI Object types.
|
||||
//
|
||||
// This class provides core operations for working with the Rust `Arc<T>` pointer to
|
||||
// the live Rust struct on the other side of the FFI.
|
||||
//
|
||||
// There's some subtlety here, because we have to be careful not to operate on a Rust
|
||||
// struct after it has been dropped, and because we must expose a public API for freeing
|
||||
// the Kotlin wrapper object in lieu of reliable finalizers. The core requirements are:
|
||||
//
|
||||
// * Each `FFIObject` instance holds an opaque pointer to the underlying Rust struct.
|
||||
// Method calls need to read this pointer from the object's state and pass it in to
|
||||
// the Rust FFI.
|
||||
//
|
||||
// * When an `FFIObject` is no longer needed, its pointer should be passed to a
|
||||
// special destructor function provided by the Rust FFI, which will drop the
|
||||
// underlying Rust struct.
|
||||
//
|
||||
// * Given an `FFIObject` instance, calling code is expected to call the special
|
||||
// `destroy` method in order to free it after use, either by calling it explicitly
|
||||
// or by using a higher-level helper like the `use` method. Failing to do so will
|
||||
// leak the underlying Rust struct.
|
||||
//
|
||||
// * We can't assume that calling code will do the right thing, and must be prepared
|
||||
// to handle Kotlin method calls executing concurrently with or even after a call to
|
||||
// `destroy`, and to handle multiple (possibly concurrent!) calls to `destroy`.
|
||||
//
|
||||
// * We must never allow Rust code to operate on the underlying Rust struct after
|
||||
// the destructor has been called, and must never call the destructor more than once.
|
||||
// Doing so may trigger memory unsafety.
|
||||
//
|
||||
// If we try to implement this with mutual exclusion on access to the pointer, there is the
|
||||
// possibility of a race between a method call and a concurrent call to `destroy`:
|
||||
//
|
||||
// * Thread A starts a method call, reads the value of the pointer, but is interrupted
|
||||
// before it can pass the pointer over the FFI to Rust.
|
||||
// * Thread B calls `destroy` and frees the underlying Rust struct.
|
||||
// * Thread A resumes, passing the already-read pointer value to Rust and triggering
|
||||
// a use-after-free.
|
||||
//
|
||||
// One possible solution would be to use a `ReadWriteLock`, with each method call taking
|
||||
// a read lock (and thus allowed to run concurrently) and the special `destroy` method
|
||||
// taking a write lock (and thus blocking on live method calls). However, we aim not to
|
||||
// generate methods with any hidden blocking semantics, and a `destroy` method that might
|
||||
// block if called incorrectly seems to meet that bar.
|
||||
//
|
||||
// So, we achieve our goals by giving each `FFIObject` an associated `AtomicLong` counter to track
|
||||
// the number of in-flight method calls, and an `AtomicBoolean` flag to indicate whether `destroy`
|
||||
// has been called. These are updated according to the following rules:
|
||||
//
|
||||
// * The initial value of the counter is 1, indicating a live object with no in-flight calls.
|
||||
// The initial value for the flag is false.
|
||||
//
|
||||
// * At the start of each method call, we atomically check the counter.
|
||||
// If it is 0 then the underlying Rust struct has already been destroyed and the call is aborted.
|
||||
// If it is nonzero them we atomically increment it by 1 and proceed with the method call.
|
||||
//
|
||||
// * At the end of each method call, we atomically decrement and check the counter.
|
||||
// If it has reached zero then we destroy the underlying Rust struct.
|
||||
//
|
||||
// * When `destroy` is called, we atomically flip the flag from false to true.
|
||||
// If the flag was already true we silently fail.
|
||||
// Otherwise we atomically decrement and check the counter.
|
||||
// If it has reached zero then we destroy the underlying Rust struct.
|
||||
//
|
||||
// Astute readers may observe that this all sounds very similar to the way that Rust's `Arc<T>` works,
|
||||
// and indeed it is, with the addition of a flag to guard against multiple calls to `destroy`.
|
||||
//
|
||||
// The overall effect is that the underlying Rust struct is destroyed only when `destroy` has been
|
||||
// called *and* all in-flight method calls have completed, avoiding violating any of the expectations
|
||||
// of the underlying Rust code.
|
||||
//
|
||||
// In the future we may be able to replace some of this with automatic finalization logic, such as using
|
||||
// the new "Cleaner" functionaility in Java 9. The above scheme has been designed to work even if `destroy` is
|
||||
// invoked by garbage-collection machinery rather than by calling code (which by the way, it's apparently also
|
||||
// possible for the JVM to finalize an object while there is an in-flight call to one of its methods [1],
|
||||
// so there would still be some complexity here).
|
||||
//
|
||||
// Sigh...all of this for want of a robust finalization mechanism.
|
||||
//
|
||||
// [1] https://stackoverflow.com/questions/24376768/can-java-finalize-an-object-when-it-is-still-in-scope/24380219
|
||||
//
|
||||
abstract class FFIObject(
|
||||
protected val pointer: Pointer
|
||||
): Disposable, AutoCloseable {
|
||||
|
||||
private val wasDestroyed = AtomicBoolean(false)
|
||||
private val callCounter = AtomicLong(1)
|
||||
|
||||
open protected fun freeRustArcPtr() {
|
||||
// To be overridden in subclasses.
|
||||
}
|
||||
|
||||
override fun destroy() {
|
||||
// Only allow a single call to this method.
|
||||
// TODO: maybe we should log a warning if called more than once?
|
||||
if (this.wasDestroyed.compareAndSet(false, true)) {
|
||||
// This decrement always matches the initial count of 1 given at creation time.
|
||||
if (this.callCounter.decrementAndGet() == 0L) {
|
||||
this.freeRustArcPtr()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
override fun close() {
|
||||
this.destroy()
|
||||
}
|
||||
|
||||
internal inline fun <R> callWithPointer(block: (ptr: Pointer) -> R): R {
|
||||
// Check and increment the call counter, to keep the object alive.
|
||||
// This needs a compare-and-set retry loop in case of concurrent updates.
|
||||
do {
|
||||
val c = this.callCounter.get()
|
||||
if (c == 0L) {
|
||||
throw IllegalStateException("${this.javaClass.simpleName} object has already been destroyed")
|
||||
}
|
||||
if (c == Long.MAX_VALUE) {
|
||||
throw IllegalStateException("${this.javaClass.simpleName} call counter would overflow")
|
||||
}
|
||||
} while (! this.callCounter.compareAndSet(c, c + 1L))
|
||||
// Now we can safely do the method call without the pointer being freed concurrently.
|
||||
try {
|
||||
return block(this.pointer)
|
||||
} finally {
|
||||
// This decrement always matches the increment we performed above.
|
||||
if (this.callCounter.decrementAndGet() == 0L) {
|
||||
this.freeRustArcPtr()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface EventNotifierInterface {
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
class EventNotifier(
|
||||
pointer: Pointer
|
||||
) : FFIObject(pointer), EventNotifierInterface {
|
||||
|
||||
/**
|
||||
* Disconnect the object from the underlying Rust object.
|
||||
*
|
||||
* It can be called more than once, but once called, interacting with the object
|
||||
* causes an `IllegalStateException`.
|
||||
*
|
||||
* Clients **must** call this method once done with the object, or cause a memory leak.
|
||||
*/
|
||||
override protected fun freeRustArcPtr() {
|
||||
rustCall() { status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_free_eventnotifier(this.pointer, status)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
companion object
|
||||
|
||||
}
|
||||
|
||||
public object FfiConverterTypeEventNotifier: FfiConverter<EventNotifier, Pointer> {
|
||||
override fun lower(value: EventNotifier): Pointer = value.callWithPointer { it }
|
||||
|
||||
override fun lift(value: Pointer): EventNotifier {
|
||||
return EventNotifier(value)
|
||||
}
|
||||
|
||||
override fun read(buf: ByteBuffer): EventNotifier {
|
||||
// The Rust code always writes pointers as 8 bytes, and will
|
||||
// fail to compile if they don't fit.
|
||||
return lift(Pointer(buf.getLong()))
|
||||
}
|
||||
|
||||
override fun allocationSize(value: EventNotifier) = 8
|
||||
|
||||
override fun write(value: EventNotifier, buf: ByteBuffer) {
|
||||
// The Rust code always expects pointers written as 8 bytes,
|
||||
// and will fail to compile if they don't fit.
|
||||
buf.putLong(Pointer.nativeValue(lower(value)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
internal typealias Handle = Long
|
||||
internal class ConcurrentHandleMap<T>(
|
||||
private val leftMap: MutableMap<Handle, T> = mutableMapOf(),
|
||||
private val rightMap: MutableMap<T, Handle> = mutableMapOf()
|
||||
) {
|
||||
private val lock = java.util.concurrent.locks.ReentrantLock()
|
||||
private val currentHandle = AtomicLong(0L)
|
||||
private val stride = 1L
|
||||
|
||||
fun insert(obj: T): Handle =
|
||||
lock.withLock {
|
||||
rightMap[obj] ?:
|
||||
currentHandle.getAndAdd(stride)
|
||||
.also { handle ->
|
||||
leftMap[handle] = obj
|
||||
rightMap[obj] = handle
|
||||
}
|
||||
}
|
||||
|
||||
fun get(handle: Handle) = lock.withLock {
|
||||
leftMap[handle]
|
||||
}
|
||||
|
||||
fun delete(handle: Handle) {
|
||||
this.remove(handle)
|
||||
}
|
||||
|
||||
fun remove(handle: Handle): T? =
|
||||
lock.withLock {
|
||||
leftMap.remove(handle)?.let { obj ->
|
||||
rightMap.remove(obj)
|
||||
obj
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ForeignCallback : com.sun.jna.Callback {
|
||||
public fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int
|
||||
}
|
||||
|
||||
// Magic number for the Rust proxy to call using the same mechanism as every other method,
|
||||
// to free the callback once it's dropped by Rust.
|
||||
internal const val IDX_CALLBACK_FREE = 0
|
||||
// Callback return codes
|
||||
internal const val UNIFFI_CALLBACK_SUCCESS = 0
|
||||
internal const val UNIFFI_CALLBACK_ERROR = 1
|
||||
internal const val UNIFFI_CALLBACK_UNEXPECTED_ERROR = 2
|
||||
|
||||
public abstract class FfiConverterCallbackInterface<CallbackInterface>(
|
||||
protected val foreignCallback: ForeignCallback
|
||||
): FfiConverter<CallbackInterface, Handle> {
|
||||
private val handleMap = ConcurrentHandleMap<CallbackInterface>()
|
||||
|
||||
// Registers the foreign callback with the Rust side.
|
||||
// This method is generated for each callback interface.
|
||||
internal abstract fun register(lib: _UniFFILib)
|
||||
|
||||
fun drop(handle: Handle): RustBuffer.ByValue {
|
||||
return handleMap.remove(handle).let { RustBuffer.ByValue() }
|
||||
}
|
||||
|
||||
override fun lift(value: Handle): CallbackInterface {
|
||||
return handleMap.get(value) ?: throw InternalException("No callback in handlemap; this is a Uniffi bug")
|
||||
}
|
||||
|
||||
override fun read(buf: ByteBuffer) = lift(buf.getLong())
|
||||
|
||||
override fun lower(value: CallbackInterface) =
|
||||
handleMap.insert(value).also {
|
||||
assert(handleMap.get(it) === value) { "Handle map is not returning the object we just placed there. This is a bug in the HandleMap." }
|
||||
}
|
||||
|
||||
override fun allocationSize(value: CallbackInterface) = 8
|
||||
|
||||
override fun write(value: CallbackInterface, buf: ByteBuffer) {
|
||||
buf.putLong(lower(value))
|
||||
}
|
||||
}
|
||||
|
||||
// Declaration and FfiConverters for EventListener Callback Interface
|
||||
|
||||
public interface EventListener {
|
||||
fun `onEventOccurred`(`eventData`: String)
|
||||
|
||||
companion object
|
||||
}
|
||||
|
||||
// The ForeignCallback that is passed to Rust.
|
||||
internal class ForeignCallbackTypeEventListener : ForeignCallback {
|
||||
@Suppress("TooGenericExceptionCaught")
|
||||
override fun callback(handle: Handle, method: Int, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int {
|
||||
val cb = FfiConverterTypeEventListener.lift(handle)
|
||||
return when (method) {
|
||||
IDX_CALLBACK_FREE -> {
|
||||
FfiConverterTypeEventListener.drop(handle)
|
||||
// Successful return
|
||||
// See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
|
||||
UNIFFI_CALLBACK_SUCCESS
|
||||
}
|
||||
1 -> {
|
||||
// Call the method, write to outBuf and return a status code
|
||||
// See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs` for info
|
||||
try {
|
||||
this.`invokeOnEventOccurred`(cb, argsData, argsLen, outBuf)
|
||||
} catch (e: Throwable) {
|
||||
// Unexpected error
|
||||
try {
|
||||
// Try to serialize the error into a string
|
||||
outBuf.setValue(FfiConverterString.lower(e.toString()))
|
||||
} catch (e: Throwable) {
|
||||
// If that fails, then it's time to give up and just return
|
||||
}
|
||||
UNIFFI_CALLBACK_UNEXPECTED_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
// An unexpected error happened.
|
||||
// See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
|
||||
try {
|
||||
// Try to serialize the error into a string
|
||||
outBuf.setValue(FfiConverterString.lower("Invalid Callback index"))
|
||||
} catch (e: Throwable) {
|
||||
// If that fails, then it's time to give up and just return
|
||||
}
|
||||
UNIFFI_CALLBACK_UNEXPECTED_ERROR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
private fun `invokeOnEventOccurred`(kotlinCallbackInterface: EventListener, argsData: Pointer, argsLen: Int, outBuf: RustBufferByReference): Int {
|
||||
val argsBuf = argsData.getByteBuffer(0, argsLen.toLong()).also {
|
||||
it.order(ByteOrder.BIG_ENDIAN)
|
||||
}
|
||||
fun makeCall() : Int {
|
||||
kotlinCallbackInterface.`onEventOccurred`(
|
||||
FfiConverterString.read(argsBuf)
|
||||
)
|
||||
return UNIFFI_CALLBACK_SUCCESS
|
||||
}
|
||||
fun makeCallAndHandleError() : Int = makeCall()
|
||||
|
||||
return makeCallAndHandleError()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// The ffiConverter which transforms the Callbacks in to Handles to pass to Rust.
|
||||
public object FfiConverterTypeEventListener: FfiConverterCallbackInterface<EventListener>(
|
||||
foreignCallback = ForeignCallbackTypeEventListener()
|
||||
) {
|
||||
override fun register(lib: _UniFFILib) {
|
||||
rustCall() { status ->
|
||||
lib.uniffi_pubkymobile_fn_init_callback_eventlistener(this.foreignCallback, status)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public object FfiConverterSequenceString: FfiConverterRustBuffer<List<String>> {
|
||||
@@ -730,10 +1130,6 @@ public object FfiConverterSequenceString: FfiConverterRustBuffer<List<String>> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fun `auth`(`url`: String, `secretKey`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
@@ -742,20 +1138,54 @@ fun `auth`(`url`: String, `secretKey`: String): List<String> {
|
||||
}
|
||||
|
||||
|
||||
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
||||
suspend fun `get`(`url`: String) : List<String> {
|
||||
return uniffiRustCallAsync(
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_get(FfiConverterString.lower(`url`),),
|
||||
{ future, continuation -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_poll_rust_buffer(future, continuation) },
|
||||
{ future, continuation -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_complete_rust_buffer(future, continuation) },
|
||||
{ future -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_free_rust_buffer(future) },
|
||||
// lift function
|
||||
{ FfiConverterSequenceString.lift(it) },
|
||||
// Error FFI converter
|
||||
NullCallStatusErrorHandler,
|
||||
)
|
||||
fun `createRecoveryFile`(`secretKey`: String, `passphrase`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_create_recovery_file(FfiConverterString.lower(`secretKey`),FfiConverterString.lower(`passphrase`),_status)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun `decryptRecoveryFile`(`recoveryFile`: String, `passphrase`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_decrypt_recovery_file(FfiConverterString.lower(`recoveryFile`),FfiConverterString.lower(`passphrase`),_status)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun `deleteFile`(`url`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_delete_file(FfiConverterString.lower(`url`),_status)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun `generateSecretKey`(): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_generate_secret_key(_status)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun `get`(`url`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_get(FfiConverterString.lower(`url`),_status)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun `getPublicKeyFromSecretKey`(`secretKey`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_get_public_key_from_secret_key(FfiConverterString.lower(`secretKey`),_status)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun `list`(`url`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
@@ -788,20 +1218,22 @@ fun `publishHttps`(`recordName`: String, `target`: String, `secretKey`: String):
|
||||
}
|
||||
|
||||
|
||||
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
||||
suspend fun `put`(`url`: String, `content`: String) : List<String> {
|
||||
return uniffiRustCallAsync(
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_put(FfiConverterString.lower(`url`),FfiConverterString.lower(`content`),),
|
||||
{ future, continuation -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_poll_rust_buffer(future, continuation) },
|
||||
{ future, continuation -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_complete_rust_buffer(future, continuation) },
|
||||
{ future -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_free_rust_buffer(future) },
|
||||
// lift function
|
||||
{ FfiConverterSequenceString.lift(it) },
|
||||
// Error FFI converter
|
||||
NullCallStatusErrorHandler,
|
||||
)
|
||||
fun `put`(`url`: String, `content`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_put(FfiConverterString.lower(`url`),FfiConverterString.lower(`content`),_status)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun `removeEventListener`() =
|
||||
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_remove_event_listener(_status)
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun `resolve`(`publicKey`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
@@ -818,45 +1250,43 @@ fun `resolveHttps`(`publicKey`: String): List<String> {
|
||||
}
|
||||
|
||||
|
||||
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
||||
suspend fun `signIn`(`secretKey`: String) : List<String> {
|
||||
return uniffiRustCallAsync(
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_sign_in(FfiConverterString.lower(`secretKey`),),
|
||||
{ future, continuation -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_poll_rust_buffer(future, continuation) },
|
||||
{ future, continuation -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_complete_rust_buffer(future, continuation) },
|
||||
{ future -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_free_rust_buffer(future) },
|
||||
// lift function
|
||||
{ FfiConverterSequenceString.lift(it) },
|
||||
// Error FFI converter
|
||||
NullCallStatusErrorHandler,
|
||||
)
|
||||
fun `session`(`pubky`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_session(FfiConverterString.lower(`pubky`),_status)
|
||||
})
|
||||
}
|
||||
|
||||
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
||||
suspend fun `signOut`(`secretKey`: String) : List<String> {
|
||||
return uniffiRustCallAsync(
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_sign_out(FfiConverterString.lower(`secretKey`),),
|
||||
{ future, continuation -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_poll_rust_buffer(future, continuation) },
|
||||
{ future, continuation -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_complete_rust_buffer(future, continuation) },
|
||||
{ future -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_free_rust_buffer(future) },
|
||||
// lift function
|
||||
{ FfiConverterSequenceString.lift(it) },
|
||||
// Error FFI converter
|
||||
NullCallStatusErrorHandler,
|
||||
)
|
||||
|
||||
fun `setEventListener`(`listener`: EventListener) =
|
||||
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_set_event_listener(FfiConverterTypeEventListener.lower(`listener`),_status)
|
||||
}
|
||||
|
||||
@Suppress("ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE")
|
||||
suspend fun `signUp`(`secretKey`: String, `homeserver`: String) : List<String> {
|
||||
return uniffiRustCallAsync(
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_sign_up(FfiConverterString.lower(`secretKey`),FfiConverterString.lower(`homeserver`),),
|
||||
{ future, continuation -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_poll_rust_buffer(future, continuation) },
|
||||
{ future, continuation -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_complete_rust_buffer(future, continuation) },
|
||||
{ future -> _UniFFILib.INSTANCE.ffi_pubkymobile_rust_future_free_rust_buffer(future) },
|
||||
// lift function
|
||||
{ FfiConverterSequenceString.lift(it) },
|
||||
// Error FFI converter
|
||||
NullCallStatusErrorHandler,
|
||||
)
|
||||
|
||||
|
||||
fun `signIn`(`secretKey`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_sign_in(FfiConverterString.lower(`secretKey`),_status)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun `signOut`(`secretKey`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_sign_out(FfiConverterString.lower(`secretKey`),_status)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
fun `signUp`(`secretKey`: String, `homeserver`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_sign_up(FfiConverterString.lower(`secretKey`),FfiConverterString.lower(`homeserver`),_status)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1237,7 +1237,7 @@ PODS:
|
||||
- ReactCommon/turbomodule/bridging
|
||||
- ReactCommon/turbomodule/core
|
||||
- Yoga
|
||||
- react-native-pubky (0.8.0):
|
||||
- react-native-pubky (0.9.0):
|
||||
- DoubleConversion
|
||||
- glog
|
||||
- hermes-engine
|
||||
@@ -1757,7 +1757,7 @@ SPEC CHECKSUMS:
|
||||
React-logger: 4072f39df335ca443932e0ccece41fbeb5ca8404
|
||||
React-Mapbuffer: 714f2fae68edcabfc332b754e9fbaa8cfc68fdd4
|
||||
React-microtasksnativemodule: 4943ad8f99be8ccf5a63329fa7d269816609df9e
|
||||
react-native-pubky: 1da8f3324a665ecf4495652efe0e67820a22f3a2
|
||||
react-native-pubky: 56f7277970ed798732a1268a6fac2fe4f8517c25
|
||||
React-nativeconfig: 4a9543185905fe41014c06776bf126083795aed9
|
||||
React-NativeModulesApple: 0506da59fc40d2e1e6e12a233db5e81c46face27
|
||||
React-perflogger: 3bbb82f18e9ac29a1a6931568e99d6305ef4403b
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { StyleSheet, View, Button } from 'react-native';
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
auth,
|
||||
parseAuthUrl,
|
||||
@@ -16,6 +17,10 @@ import {
|
||||
getPublicKeyFromSecretKey,
|
||||
decryptRecoveryFile,
|
||||
createRecoveryFile,
|
||||
setEventListener,
|
||||
removeEventListener,
|
||||
session,
|
||||
deleteFile,
|
||||
} from '@synonymdev/react-native-pubky';
|
||||
|
||||
const HOMESERVER = '8pinxxgqs41n4aididenw5apqp1urfmzdztr8jt4abrkdn435ewo';
|
||||
@@ -24,6 +29,34 @@ const SECRET_KEY =
|
||||
const PUBLIC_KEY = 'z4e8s17cou9qmuwen8p1556jzhf1wktmzo6ijsfnri9c4hnrdfty';
|
||||
|
||||
export default function App() {
|
||||
useEffect(() => {
|
||||
let cleanupListener: (() => void) | undefined;
|
||||
|
||||
const setupEventListener = async () => {
|
||||
const result = await setEventListener((eventData) => {
|
||||
console.log('Received event:', eventData);
|
||||
});
|
||||
|
||||
if (result.isOk()) {
|
||||
console.log('Event listener set up successfully');
|
||||
} else {
|
||||
console.error('Failed to set up event listener:', result.error);
|
||||
}
|
||||
|
||||
cleanupListener = () => {
|
||||
removeEventListener();
|
||||
};
|
||||
};
|
||||
|
||||
setupEventListener();
|
||||
|
||||
// Cleanup function
|
||||
return () => {
|
||||
if (cleanupListener) {
|
||||
cleanupListener();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Button
|
||||
@@ -237,6 +270,51 @@ export default function App() {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title={'deleteFile'}
|
||||
onPress={async (): Promise<void> => {
|
||||
try {
|
||||
const listRes = await list(
|
||||
`pubky://${PUBLIC_KEY}/pub/synonym.to` // URL
|
||||
);
|
||||
if (listRes.isErr()) {
|
||||
console.log(listRes.error.message);
|
||||
return;
|
||||
}
|
||||
if (!listRes.value.length || !listRes.value[0]) {
|
||||
console.log('No files found');
|
||||
return;
|
||||
}
|
||||
const res = await deleteFile(
|
||||
listRes.value[0] // URL
|
||||
);
|
||||
if (res.isErr()) {
|
||||
console.log(res.error.message);
|
||||
return;
|
||||
}
|
||||
console.log(res.value);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title={'session'}
|
||||
onPress={async (): Promise<void> => {
|
||||
try {
|
||||
const res = await session(
|
||||
PUBLIC_KEY // Public key
|
||||
);
|
||||
if (res.isErr()) {
|
||||
console.log(res.error.message);
|
||||
return;
|
||||
}
|
||||
console.log(res.value);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title={'generateSecretKey'}
|
||||
onPress={async (): Promise<void> => {
|
||||
|
||||
@@ -63,12 +63,18 @@ typedef struct RustCallStatus {
|
||||
typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
|
||||
|
||||
// Scaffolding functions
|
||||
void uniffi_pubkymobile_fn_free_eventnotifier(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
void uniffi_pubkymobile_fn_init_callback_eventlistener(ForeignCallback _Nonnull callback_stub, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_auth(RustBuffer url, RustBuffer secret_key, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_create_recovery_file(RustBuffer secret_key, RustBuffer passphrase, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_decrypt_recovery_file(RustBuffer recovery_file, RustBuffer passphrase, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_delete_file(RustBuffer url, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_generate_secret_key(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
@@ -85,11 +91,18 @@ RustBuffer uniffi_pubkymobile_fn_func_publish(RustBuffer record_name, RustBuffer
|
||||
RustBuffer uniffi_pubkymobile_fn_func_publish_https(RustBuffer record_name, RustBuffer target, RustBuffer secret_key, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_put(RustBuffer url, RustBuffer content, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
void uniffi_pubkymobile_fn_func_remove_event_listener(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_resolve(RustBuffer public_key, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_resolve_https(RustBuffer public_key, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_session(RustBuffer pubky, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
void uniffi_pubkymobile_fn_func_set_event_listener(uint64_t listener, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_sign_in(RustBuffer secret_key, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_sign_out(RustBuffer secret_key, RustCallStatus *_Nonnull out_status
|
||||
@@ -218,6 +231,9 @@ uint16_t uniffi_pubkymobile_checksum_func_create_recovery_file(void
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_decrypt_recovery_file(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_delete_file(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_generate_secret_key(void
|
||||
|
||||
@@ -242,12 +258,21 @@ uint16_t uniffi_pubkymobile_checksum_func_publish_https(void
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_put(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_remove_event_listener(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_resolve(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_resolve_https(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_session(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_set_event_listener(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_sign_in(void
|
||||
|
||||
@@ -257,6 +282,9 @@ uint16_t uniffi_pubkymobile_checksum_func_sign_out(void
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_sign_up(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_method_eventlistener_on_event_occurred(void
|
||||
|
||||
);
|
||||
uint32_t ffi_pubkymobile_uniffi_contract_version(void
|
||||
|
||||
|
||||
Binary file not shown.
@@ -63,12 +63,18 @@ typedef struct RustCallStatus {
|
||||
typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
|
||||
|
||||
// Scaffolding functions
|
||||
void uniffi_pubkymobile_fn_free_eventnotifier(void*_Nonnull ptr, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
void uniffi_pubkymobile_fn_init_callback_eventlistener(ForeignCallback _Nonnull callback_stub, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_auth(RustBuffer url, RustBuffer secret_key, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_create_recovery_file(RustBuffer secret_key, RustBuffer passphrase, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_decrypt_recovery_file(RustBuffer recovery_file, RustBuffer passphrase, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_delete_file(RustBuffer url, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_generate_secret_key(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
@@ -85,11 +91,18 @@ RustBuffer uniffi_pubkymobile_fn_func_publish(RustBuffer record_name, RustBuffer
|
||||
RustBuffer uniffi_pubkymobile_fn_func_publish_https(RustBuffer record_name, RustBuffer target, RustBuffer secret_key, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_put(RustBuffer url, RustBuffer content, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
void uniffi_pubkymobile_fn_func_remove_event_listener(RustCallStatus *_Nonnull out_status
|
||||
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_resolve(RustBuffer public_key, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_resolve_https(RustBuffer public_key, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_session(RustBuffer pubky, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
void uniffi_pubkymobile_fn_func_set_event_listener(uint64_t listener, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_sign_in(RustBuffer secret_key, RustCallStatus *_Nonnull out_status
|
||||
);
|
||||
RustBuffer uniffi_pubkymobile_fn_func_sign_out(RustBuffer secret_key, RustCallStatus *_Nonnull out_status
|
||||
@@ -218,6 +231,9 @@ uint16_t uniffi_pubkymobile_checksum_func_create_recovery_file(void
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_decrypt_recovery_file(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_delete_file(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_generate_secret_key(void
|
||||
|
||||
@@ -242,12 +258,21 @@ uint16_t uniffi_pubkymobile_checksum_func_publish_https(void
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_put(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_remove_event_listener(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_resolve(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_resolve_https(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_session(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_set_event_listener(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_sign_in(void
|
||||
|
||||
@@ -257,6 +282,9 @@ uint16_t uniffi_pubkymobile_checksum_func_sign_out(void
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_func_sign_up(void
|
||||
|
||||
);
|
||||
uint16_t uniffi_pubkymobile_checksum_method_eventlistener_on_event_occurred(void
|
||||
|
||||
);
|
||||
uint32_t ffi_pubkymobile_uniffi_contract_version(void
|
||||
|
||||
|
||||
Binary file not shown.
@@ -1,2 +1,3 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTViewManager.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
17
ios/Pubky.mm
17
ios/Pubky.mm
@@ -1,6 +1,21 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@interface RCT_EXTERN_MODULE(Pubky, NSObject)
|
||||
@interface RCT_EXTERN_MODULE(Pubky, RCTEventEmitter)
|
||||
|
||||
RCT_EXTERN_METHOD(setEventListener:(RCTPromiseResolveBlock)resolve
|
||||
withRejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(removeEventListener:(RCTPromiseResolveBlock)resolve
|
||||
withRejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(deleteFile:(NSString *)url
|
||||
withResolver:(RCTPromiseResolveBlock)resolve
|
||||
withRejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(session:(NSString *)pubky
|
||||
withResolver:(RCTPromiseResolveBlock)resolve
|
||||
withRejecter:(RCTPromiseRejectBlock)reject)
|
||||
|
||||
RCT_EXTERN_METHOD(auth:(NSString *)url
|
||||
secretKey:(NSString *)secretKey
|
||||
|
||||
@@ -1,7 +1,46 @@
|
||||
import Foundation
|
||||
import React
|
||||
|
||||
@objc(Pubky)
|
||||
class Pubky: NSObject {
|
||||
class Pubky: RCTEventEmitter {
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc override static func requiresMainQueueSetup() -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
override func supportedEvents() -> [String]! {
|
||||
return ["PubkyEvent"]
|
||||
}
|
||||
|
||||
class EventListenerImpl: EventListener {
|
||||
weak var pubky: Pubky?
|
||||
|
||||
init(pubky: Pubky) {
|
||||
self.pubky = pubky
|
||||
}
|
||||
|
||||
func onEventOccurred(eventData: String) {
|
||||
pubky?.sendEvent(withName: "PubkyEvent", body: eventData)
|
||||
}
|
||||
}
|
||||
|
||||
@objc(setEventListener:withRejecter:)
|
||||
func setEventListener(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
let listener = EventListenerImpl(pubky: self)
|
||||
react_native_pubky.setEventListener(listener: listener)
|
||||
resolve(nil)
|
||||
}
|
||||
|
||||
@objc(removeEventListener:withRejecter:)
|
||||
func removeEventListener(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
react_native_pubky.removeEventListener()
|
||||
resolve(nil)
|
||||
}
|
||||
|
||||
@objc(auth:secretKey:withResolver:withRejecter:)
|
||||
func auth(_ url: String, secretKey: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
Task {
|
||||
@@ -144,6 +183,30 @@ class Pubky: NSObject {
|
||||
}
|
||||
}
|
||||
|
||||
@objc(deleteFile:withResolver:withRejecter:)
|
||||
func deleteFile(_ url: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
Task {
|
||||
do {
|
||||
let result = try await react_native_pubky.deleteFile(url: url)
|
||||
resolve(result)
|
||||
} catch {
|
||||
reject("list Error", "Failed to deleteFile", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc(session:withResolver:withRejecter:)
|
||||
func session(_ pubky: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
Task {
|
||||
do {
|
||||
let result = react_native_pubky.session(pubky: pubky)
|
||||
resolve(result)
|
||||
} catch {
|
||||
reject("session Error", "Failed to get session", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc(generateSecretKey:withRejecter:)
|
||||
func generateSecretKey(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
||||
Task {
|
||||
|
||||
@@ -335,6 +335,240 @@ fileprivate struct FfiConverterString: FfiConverter {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public protocol EventNotifierProtocol {
|
||||
|
||||
}
|
||||
|
||||
public class EventNotifier: EventNotifierProtocol {
|
||||
fileprivate let pointer: UnsafeMutableRawPointer
|
||||
|
||||
// TODO: We'd like this to be `private` but for Swifty reasons,
|
||||
// we can't implement `FfiConverter` without making this `required` and we can't
|
||||
// make it `required` without making it `public`.
|
||||
required init(unsafeFromRawPointer pointer: UnsafeMutableRawPointer) {
|
||||
self.pointer = pointer
|
||||
}
|
||||
|
||||
deinit {
|
||||
try! rustCall { uniffi_pubkymobile_fn_free_eventnotifier(pointer, $0) }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
public struct FfiConverterTypeEventNotifier: FfiConverter {
|
||||
typealias FfiType = UnsafeMutableRawPointer
|
||||
typealias SwiftType = EventNotifier
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> EventNotifier {
|
||||
let v: UInt64 = try readInt(&buf)
|
||||
// The Rust code won't compile if a pointer won't fit in a UInt64.
|
||||
// We have to go via `UInt` because that's the thing that's the size of a pointer.
|
||||
let ptr = UnsafeMutableRawPointer(bitPattern: UInt(truncatingIfNeeded: v))
|
||||
if (ptr == nil) {
|
||||
throw UniffiInternalError.unexpectedNullPointer
|
||||
}
|
||||
return try lift(ptr!)
|
||||
}
|
||||
|
||||
public static func write(_ value: EventNotifier, into buf: inout [UInt8]) {
|
||||
// This fiddling is because `Int` is the thing that's the same size as a pointer.
|
||||
// The Rust code won't compile if a pointer won't fit in a `UInt64`.
|
||||
writeInt(&buf, UInt64(bitPattern: Int64(Int(bitPattern: lower(value)))))
|
||||
}
|
||||
|
||||
public static func lift(_ pointer: UnsafeMutableRawPointer) throws -> EventNotifier {
|
||||
return EventNotifier(unsafeFromRawPointer: pointer)
|
||||
}
|
||||
|
||||
public static func lower(_ value: EventNotifier) -> UnsafeMutableRawPointer {
|
||||
return value.pointer
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public func FfiConverterTypeEventNotifier_lift(_ pointer: UnsafeMutableRawPointer) throws -> EventNotifier {
|
||||
return try FfiConverterTypeEventNotifier.lift(pointer)
|
||||
}
|
||||
|
||||
public func FfiConverterTypeEventNotifier_lower(_ value: EventNotifier) -> UnsafeMutableRawPointer {
|
||||
return FfiConverterTypeEventNotifier.lower(value)
|
||||
}
|
||||
|
||||
fileprivate extension NSLock {
|
||||
func withLock<T>(f: () throws -> T) rethrows -> T {
|
||||
self.lock()
|
||||
defer { self.unlock() }
|
||||
return try f()
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate typealias UniFFICallbackHandle = UInt64
|
||||
fileprivate class UniFFICallbackHandleMap<T> {
|
||||
private var leftMap: [UniFFICallbackHandle: T] = [:]
|
||||
private var counter: [UniFFICallbackHandle: UInt64] = [:]
|
||||
private var rightMap: [ObjectIdentifier: UniFFICallbackHandle] = [:]
|
||||
|
||||
private let lock = NSLock()
|
||||
private var currentHandle: UniFFICallbackHandle = 0
|
||||
private let stride: UniFFICallbackHandle = 1
|
||||
|
||||
func insert(obj: T) -> UniFFICallbackHandle {
|
||||
lock.withLock {
|
||||
let id = ObjectIdentifier(obj as AnyObject)
|
||||
let handle = rightMap[id] ?? {
|
||||
currentHandle += stride
|
||||
let handle = currentHandle
|
||||
leftMap[handle] = obj
|
||||
rightMap[id] = handle
|
||||
return handle
|
||||
}()
|
||||
counter[handle] = (counter[handle] ?? 0) + 1
|
||||
return handle
|
||||
}
|
||||
}
|
||||
|
||||
func get(handle: UniFFICallbackHandle) -> T? {
|
||||
lock.withLock {
|
||||
leftMap[handle]
|
||||
}
|
||||
}
|
||||
|
||||
func delete(handle: UniFFICallbackHandle) {
|
||||
remove(handle: handle)
|
||||
}
|
||||
|
||||
@discardableResult
|
||||
func remove(handle: UniFFICallbackHandle) -> T? {
|
||||
lock.withLock {
|
||||
defer { counter[handle] = (counter[handle] ?? 1) - 1 }
|
||||
guard counter[handle] == 1 else { return leftMap[handle] }
|
||||
let obj = leftMap.removeValue(forKey: handle)
|
||||
if let obj = obj {
|
||||
rightMap.removeValue(forKey: ObjectIdentifier(obj as AnyObject))
|
||||
}
|
||||
return obj
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Magic number for the Rust proxy to call using the same mechanism as every other method,
|
||||
// to free the callback once it's dropped by Rust.
|
||||
private let IDX_CALLBACK_FREE: Int32 = 0
|
||||
// Callback return codes
|
||||
private let UNIFFI_CALLBACK_SUCCESS: Int32 = 0
|
||||
private let UNIFFI_CALLBACK_ERROR: Int32 = 1
|
||||
private let UNIFFI_CALLBACK_UNEXPECTED_ERROR: Int32 = 2
|
||||
|
||||
// Declaration and FfiConverters for EventListener Callback Interface
|
||||
|
||||
public protocol EventListener : AnyObject {
|
||||
func onEventOccurred(eventData: String)
|
||||
|
||||
}
|
||||
|
||||
// The ForeignCallback that is passed to Rust.
|
||||
fileprivate let foreignCallbackCallbackInterfaceEventListener : ForeignCallback =
|
||||
{ (handle: UniFFICallbackHandle, method: Int32, argsData: UnsafePointer<UInt8>, argsLen: Int32, out_buf: UnsafeMutablePointer<RustBuffer>) -> Int32 in
|
||||
|
||||
|
||||
func invokeOnEventOccurred(_ swiftCallbackInterface: EventListener, _ argsData: UnsafePointer<UInt8>, _ argsLen: Int32, _ out_buf: UnsafeMutablePointer<RustBuffer>) throws -> Int32 {
|
||||
var reader = createReader(data: Data(bytes: argsData, count: Int(argsLen)))
|
||||
func makeCall() throws -> Int32 {
|
||||
try swiftCallbackInterface.onEventOccurred(
|
||||
eventData: try FfiConverterString.read(from: &reader)
|
||||
)
|
||||
return UNIFFI_CALLBACK_SUCCESS
|
||||
}
|
||||
return try makeCall()
|
||||
}
|
||||
|
||||
|
||||
switch method {
|
||||
case IDX_CALLBACK_FREE:
|
||||
FfiConverterCallbackInterfaceEventListener.drop(handle: handle)
|
||||
// Sucessful return
|
||||
// See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
|
||||
return UNIFFI_CALLBACK_SUCCESS
|
||||
case 1:
|
||||
let cb: EventListener
|
||||
do {
|
||||
cb = try FfiConverterCallbackInterfaceEventListener.lift(handle)
|
||||
} catch {
|
||||
out_buf.pointee = FfiConverterString.lower("EventListener: Invalid handle")
|
||||
return UNIFFI_CALLBACK_UNEXPECTED_ERROR
|
||||
}
|
||||
do {
|
||||
return try invokeOnEventOccurred(cb, argsData, argsLen, out_buf)
|
||||
} catch let error {
|
||||
out_buf.pointee = FfiConverterString.lower(String(describing: error))
|
||||
return UNIFFI_CALLBACK_UNEXPECTED_ERROR
|
||||
}
|
||||
|
||||
// This should never happen, because an out of bounds method index won't
|
||||
// ever be used. Once we can catch errors, we should return an InternalError.
|
||||
// https://github.com/mozilla/uniffi-rs/issues/351
|
||||
default:
|
||||
// An unexpected error happened.
|
||||
// See docs of ForeignCallback in `uniffi_core/src/ffi/foreigncallbacks.rs`
|
||||
return UNIFFI_CALLBACK_UNEXPECTED_ERROR
|
||||
}
|
||||
}
|
||||
|
||||
// FfiConverter protocol for callback interfaces
|
||||
fileprivate struct FfiConverterCallbackInterfaceEventListener {
|
||||
private static let initCallbackOnce: () = {
|
||||
// Swift ensures this initializer code will once run once, even when accessed by multiple threads.
|
||||
try! rustCall { (err: UnsafeMutablePointer<RustCallStatus>) in
|
||||
uniffi_pubkymobile_fn_init_callback_eventlistener(foreignCallbackCallbackInterfaceEventListener, err)
|
||||
}
|
||||
}()
|
||||
|
||||
private static func ensureCallbackinitialized() {
|
||||
_ = initCallbackOnce
|
||||
}
|
||||
|
||||
static func drop(handle: UniFFICallbackHandle) {
|
||||
handleMap.remove(handle: handle)
|
||||
}
|
||||
|
||||
private static var handleMap = UniFFICallbackHandleMap<EventListener>()
|
||||
}
|
||||
|
||||
extension FfiConverterCallbackInterfaceEventListener : FfiConverter {
|
||||
typealias SwiftType = EventListener
|
||||
// We can use Handle as the FfiType because it's a typealias to UInt64
|
||||
typealias FfiType = UniFFICallbackHandle
|
||||
|
||||
public static func lift(_ handle: UniFFICallbackHandle) throws -> SwiftType {
|
||||
ensureCallbackinitialized();
|
||||
guard let callback = handleMap.get(handle: handle) else {
|
||||
throw UniffiInternalError.unexpectedStaleHandle
|
||||
}
|
||||
return callback
|
||||
}
|
||||
|
||||
public static func read(from buf: inout (data: Data, offset: Data.Index)) throws -> SwiftType {
|
||||
ensureCallbackinitialized();
|
||||
let handle: UniFFICallbackHandle = try readInt(&buf)
|
||||
return try lift(handle)
|
||||
}
|
||||
|
||||
public static func lower(_ v: SwiftType) -> UniFFICallbackHandle {
|
||||
ensureCallbackinitialized();
|
||||
return handleMap.insert(obj: v)
|
||||
}
|
||||
|
||||
public static func write(_ v: SwiftType, into buf: inout [UInt8]) {
|
||||
ensureCallbackinitialized();
|
||||
writeInt(&buf, lower(v))
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate struct FfiConverterSequenceString: FfiConverterRustBuffer {
|
||||
typealias SwiftType = [String]
|
||||
|
||||
@@ -387,6 +621,15 @@ public func decryptRecoveryFile(recoveryFile: String, passphrase: String) -> [S
|
||||
)
|
||||
}
|
||||
|
||||
public func deleteFile(url: String) -> [String] {
|
||||
return try! FfiConverterSequenceString.lift(
|
||||
try! rustCall() {
|
||||
uniffi_pubkymobile_fn_func_delete_file(
|
||||
FfiConverterString.lower(url),$0)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
public func generateSecretKey() -> [String] {
|
||||
return try! FfiConverterSequenceString.lift(
|
||||
try! rustCall() {
|
||||
@@ -463,6 +706,14 @@ public func put(url: String, content: String) -> [String] {
|
||||
)
|
||||
}
|
||||
|
||||
public func removeEventListener() {
|
||||
try! rustCall() {
|
||||
uniffi_pubkymobile_fn_func_remove_event_listener($0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public func resolve(publicKey: String) -> [String] {
|
||||
return try! FfiConverterSequenceString.lift(
|
||||
try! rustCall() {
|
||||
@@ -481,6 +732,24 @@ public func resolveHttps(publicKey: String) -> [String] {
|
||||
)
|
||||
}
|
||||
|
||||
public func session(pubky: String) -> [String] {
|
||||
return try! FfiConverterSequenceString.lift(
|
||||
try! rustCall() {
|
||||
uniffi_pubkymobile_fn_func_session(
|
||||
FfiConverterString.lower(pubky),$0)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
public func setEventListener(listener: EventListener) {
|
||||
try! rustCall() {
|
||||
uniffi_pubkymobile_fn_func_set_event_listener(
|
||||
FfiConverterCallbackInterfaceEventListener.lower(listener),$0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public func signIn(secretKey: String) -> [String] {
|
||||
return try! FfiConverterSequenceString.lift(
|
||||
try! rustCall() {
|
||||
@@ -533,6 +802,9 @@ private var initializationResult: InitializationResult {
|
||||
if (uniffi_pubkymobile_checksum_func_decrypt_recovery_file() != 59688) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_pubkymobile_checksum_func_delete_file() != 57905) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_pubkymobile_checksum_func_generate_secret_key() != 63116) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
@@ -557,12 +829,21 @@ private var initializationResult: InitializationResult {
|
||||
if (uniffi_pubkymobile_checksum_func_put() != 51107) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_pubkymobile_checksum_func_remove_event_listener() != 6794) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_pubkymobile_checksum_func_resolve() != 18303) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_pubkymobile_checksum_func_resolve_https() != 34593) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_pubkymobile_checksum_func_session() != 65177) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_pubkymobile_checksum_func_set_event_listener() != 19468) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_pubkymobile_checksum_func_sign_in() != 21006) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
@@ -572,6 +853,9 @@ private var initializationResult: InitializationResult {
|
||||
if (uniffi_pubkymobile_checksum_func_sign_up() != 58756) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
if (uniffi_pubkymobile_checksum_method_eventlistener_on_event_occurred() != 39865) {
|
||||
return InitializationResult.apiChecksumMismatch
|
||||
}
|
||||
|
||||
return InitializationResult.ok
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@synonymdev/react-native-pubky",
|
||||
"version": "0.8.0",
|
||||
"version": "0.9.0",
|
||||
"description": "React Native Implementation of Pubky",
|
||||
"source": "./src/index.tsx",
|
||||
"main": "./lib/commonjs/index.js",
|
||||
|
||||
117
rust/src/lib.rs
117
rust/src/lib.rs
@@ -26,9 +26,12 @@ use pkarr::dns::{Packet, ResourceRecord};
|
||||
use serde_json::json;
|
||||
use utils::*;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Duration;
|
||||
use pkarr::bytes::Bytes;
|
||||
use pubky_common::session::Session;
|
||||
use tokio::runtime::Runtime;
|
||||
use tokio::time;
|
||||
|
||||
static PUBKY_CLIENT: Lazy<Arc<PubkyClient>> = Lazy::new(|| {
|
||||
Arc::new(PubkyClient::testnet())
|
||||
@@ -40,6 +43,117 @@ static TOKIO_RUNTIME: Lazy<Arc<Runtime>> = Lazy::new(|| {
|
||||
)
|
||||
});
|
||||
|
||||
// Define the EventListener trait
|
||||
#[uniffi::export(callback_interface)]
|
||||
pub trait EventListener: Send + Sync {
|
||||
fn on_event_occurred(&self, event_data: String);
|
||||
}
|
||||
|
||||
#[derive(uniffi::Object)]
|
||||
pub struct EventNotifier {
|
||||
listener: Arc<Mutex<Option<Box<dyn EventListener>>>>,
|
||||
}
|
||||
|
||||
impl EventNotifier {
|
||||
#[uniffi::constructor]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
listener: Arc::new(Mutex::new(None)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_listener(&self, listener: Box<dyn EventListener>) {
|
||||
let mut lock = self.listener.lock().unwrap();
|
||||
*lock = Some(listener);
|
||||
}
|
||||
|
||||
pub fn remove_listener(&self) {
|
||||
let mut lock = self.listener.lock().unwrap();
|
||||
*lock = None;
|
||||
}
|
||||
|
||||
pub fn notify_event(&self, event_data: String) {
|
||||
let lock = self.listener.lock().unwrap();
|
||||
if let Some(listener) = &*lock {
|
||||
listener.on_event_occurred(event_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static EVENT_NOTIFIER: Lazy<Arc<EventNotifier>> = Lazy::new(|| {
|
||||
Arc::new(EventNotifier::new())
|
||||
});
|
||||
|
||||
#[uniffi::export]
|
||||
pub fn set_event_listener(listener: Box<dyn EventListener>) {
|
||||
EVENT_NOTIFIER.as_ref().set_listener(listener);
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
pub fn remove_event_listener() {
|
||||
EVENT_NOTIFIER.as_ref().remove_listener();
|
||||
}
|
||||
|
||||
pub fn start_internal_event_loop() {
|
||||
let event_notifier = EVENT_NOTIFIER.clone();
|
||||
let runtime = TOKIO_RUNTIME.clone();
|
||||
runtime.spawn(async move {
|
||||
let mut interval = time::interval(Duration::from_secs(2));
|
||||
loop {
|
||||
interval.tick().await;
|
||||
event_notifier.as_ref().notify_event("Internal event triggered".to_string());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
pub fn delete_file(url: String) -> Vec<String> {
|
||||
let runtime = TOKIO_RUNTIME.clone();
|
||||
runtime.block_on(async {
|
||||
let client = PUBKY_CLIENT.clone();
|
||||
let parsed_url = match Url::parse(&url) {
|
||||
Ok(url) => url,
|
||||
Err(_) => return create_response_vector(true, "Failed to parse URL".to_string()),
|
||||
};
|
||||
match client.delete(parsed_url).await {
|
||||
Ok(_) => create_response_vector(false, "Deleted successfully".to_string()),
|
||||
Err(error) => create_response_vector(true, format!("Failed to delete: {}", error)),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
pub fn session(pubky: String) -> Vec<String> {
|
||||
let runtime = TOKIO_RUNTIME.clone();
|
||||
runtime.block_on(async {
|
||||
let client = PUBKY_CLIENT.clone();
|
||||
let public_key = match PublicKey::try_from(pubky) {
|
||||
Ok(key) => key,
|
||||
Err(error) => return create_response_vector(true, format!("Invalid homeserver public key: {}", error)),
|
||||
};
|
||||
let result = match client.session(&public_key).await {
|
||||
Ok(session) => session,
|
||||
Err(error) => return create_response_vector(true, format!("Failed to get session: {}", error)),
|
||||
};
|
||||
let session: Session = match result {
|
||||
Some(session) => session,
|
||||
None => return create_response_vector(true, "No session returned".to_string()),
|
||||
};
|
||||
|
||||
let json_obj = json!({
|
||||
"pubky": session.pubky().to_string(),
|
||||
"capabilities": session.capabilities().iter().map(|c| c.to_string()).collect::<Vec<String>>(),
|
||||
});
|
||||
|
||||
let json_str = match serde_json::to_string(&json_obj) {
|
||||
Ok(json) => json,
|
||||
Err(e) => return create_response_vector(true, format!("Failed to serialize JSON: {}", e)),
|
||||
};
|
||||
|
||||
create_response_vector(false, json_str)
|
||||
})
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
pub fn generate_secret_key() -> Vec<String> {
|
||||
let keypair = generate_keypair();
|
||||
@@ -56,6 +170,7 @@ pub fn generate_secret_key() -> Vec<String> {
|
||||
Ok(json) => json,
|
||||
Err(e) => return create_response_vector(true, format!("Failed to serialize JSON: {}", e)),
|
||||
};
|
||||
start_internal_event_loop();
|
||||
create_response_vector(false, json_str)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
import { NativeModules, Platform, NativeEventEmitter } from 'react-native';
|
||||
import { ok, err, type Result } from '@synonymdev/result';
|
||||
|
||||
const LINKING_ERROR =
|
||||
@@ -18,6 +18,30 @@ const Pubky = NativeModules.Pubky
|
||||
}
|
||||
);
|
||||
|
||||
const eventEmitter = new NativeEventEmitter(Pubky);
|
||||
|
||||
export async function setEventListener(
|
||||
callback: (eventData: string) => void
|
||||
): Promise<Result<void>> {
|
||||
try {
|
||||
await Pubky.setEventListener();
|
||||
eventEmitter.addListener('PubkyEvent', callback);
|
||||
return ok(undefined);
|
||||
} catch (e) {
|
||||
return err(JSON.stringify(e));
|
||||
}
|
||||
}
|
||||
|
||||
export async function removeEventListener(): Promise<Result<void>> {
|
||||
try {
|
||||
//await Pubky.removeEventListener();
|
||||
eventEmitter.removeAllListeners('PubkyEvent');
|
||||
return ok(undefined);
|
||||
} catch (e) {
|
||||
return err(JSON.stringify(e));
|
||||
}
|
||||
}
|
||||
|
||||
export async function auth(
|
||||
url: string,
|
||||
secretKey: string
|
||||
@@ -225,6 +249,35 @@ export async function list(url: string): Promise<Result<string[]>> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteFile(url: string): Promise<Result<string[]>> {
|
||||
try {
|
||||
const res = await Pubky.deleteFile(url);
|
||||
if (res[0] === 'error') {
|
||||
return err(res[1]);
|
||||
}
|
||||
return ok(res[1]);
|
||||
} catch (e) {
|
||||
return err(JSON.stringify(e));
|
||||
}
|
||||
}
|
||||
|
||||
interface SessionInfo {
|
||||
pubky: string;
|
||||
capabilities: string[];
|
||||
}
|
||||
|
||||
export async function session(pubky: string): Promise<Result<SessionInfo>> {
|
||||
try {
|
||||
const res = await Pubky.session(pubky);
|
||||
if (res[0] === 'error') {
|
||||
return err(res[1]);
|
||||
}
|
||||
return ok(JSON.parse(res[1]));
|
||||
} catch (e) {
|
||||
return err(JSON.stringify(e));
|
||||
}
|
||||
}
|
||||
|
||||
interface IPublicKeyInfo {
|
||||
public_key: string;
|
||||
uri: string;
|
||||
|
||||
Reference in New Issue
Block a user