diff --git a/README.md b/README.md
index 5e10e48..0e4bed6 100644
--- a/README.md
+++ b/README.md
@@ -11,8 +11,8 @@ npm install @synonymdev/react-native-pubky
## Implementation Status
### Implemented Methods
- [x] [auth](#auth): Authentication functionality.
+- [x] [parseAuthUrl](#parseAuthUrl): Method to decode an authUrl.
### Methods to be Implemented
-- [ ] parseAuthUrl: Method to decode an authUrl.
- [ ] publish: Functionality to publish content.
- [ ] resolve: Functionality to resolve content.
- [ ] signIn: Functionality to sign in.
@@ -31,6 +31,18 @@ if (authRes.isErr()) {
}
console.log(authRes.value);
```
+### parseAuthUrl
+```js
+import { parseAuthUrl } from '@synonymdev/react-native-pubky';
+
+const pubkyAuthUrl = 'pubkyauth:///?relay=https://demo.httprelay.io/link&capabilities=/pub/pubky.app:rw,/pub/example.com/nested:rw&secret=FyzJ3gJ1W7boyFZC1Do9fYrRmDNgCLNRwEu_gaBgPUA';
+const parseRes = await parseAuthUrl(pubkyAuthUrl);
+if (parseRes.isErr()) {
+ console.log(parseRes.error.message);
+ return;
+}
+console.log(parseRes.value);
+```
## Local Installation
diff --git a/android/src/main/java/com/pubky/PubkyModule.kt b/android/src/main/java/com/pubky/PubkyModule.kt
index 9d727c2..95237a4 100644
--- a/android/src/main/java/com/pubky/PubkyModule.kt
+++ b/android/src/main/java/com/pubky/PubkyModule.kt
@@ -10,6 +10,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import uniffi.pubkymobile.auth
+import uniffi.pubkymobile.parseAuthUrl
class PubkyModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
@@ -37,6 +38,19 @@ class PubkyModule(reactContext: ReactApplicationContext) :
}
}
+ @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)
+ }
+ }
+
companion object {
const val NAME = "Pubky"
}
diff --git a/android/src/main/java/uniffi/pubkymobile/pubkymobile.kt b/android/src/main/java/uniffi/pubkymobile/pubkymobile.kt
index 20cae78..46a4c9b 100644
--- a/android/src/main/java/uniffi/pubkymobile/pubkymobile.kt
+++ b/android/src/main/java/uniffi/pubkymobile/pubkymobile.kt
@@ -387,6 +387,8 @@ internal interface _UniFFILib : Library {
fun uniffi_pubkymobile_fn_func_auth(`url`: RustBuffer.ByValue,`secretKey`: RustBuffer.ByValue,
): Pointer
+ fun uniffi_pubkymobile_fn_func_parse_auth_url(`url`: 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,
@@ -503,6 +505,8 @@ internal interface _UniFFILib : Library {
): Unit
fun uniffi_pubkymobile_checksum_func_auth(
): Short
+ fun uniffi_pubkymobile_checksum_func_parse_auth_url(
+ ): Short
fun ffi_pubkymobile_uniffi_contract_version(
): Int
@@ -523,6 +527,9 @@ private fun uniffiCheckApiChecksums(lib: _UniFFILib) {
if (lib.uniffi_pubkymobile_checksum_func_auth() != 46918.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
+ if (lib.uniffi_pubkymobile_checksum_func_parse_auth_url() != 29088.toShort()) {
+ throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
+ }
}
// Async support
@@ -671,3 +678,11 @@ suspend fun `auth`(`url`: String, `secretKey`: String) : List {
)
}
+fun `parseAuthUrl`(`url`: String): List {
+ return FfiConverterSequenceString.lift(
+ rustCall() { _status ->
+ _UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_parse_auth_url(FfiConverterString.lower(`url`),_status)
+})
+}
+
+
diff --git a/android/src/main/jniLibs/arm64-v8a/libpubkymobile.so b/android/src/main/jniLibs/arm64-v8a/libpubkymobile.so
index 8c9fed6..0b51049 100755
Binary files a/android/src/main/jniLibs/arm64-v8a/libpubkymobile.so and b/android/src/main/jniLibs/arm64-v8a/libpubkymobile.so differ
diff --git a/android/src/main/jniLibs/armeabi-v7a/libpubkymobile.so b/android/src/main/jniLibs/armeabi-v7a/libpubkymobile.so
index 42b0eaf..aa8fc8f 100755
Binary files a/android/src/main/jniLibs/armeabi-v7a/libpubkymobile.so and b/android/src/main/jniLibs/armeabi-v7a/libpubkymobile.so differ
diff --git a/android/src/main/jniLibs/x86/libpubkymobile.so b/android/src/main/jniLibs/x86/libpubkymobile.so
index f330c3a..6623f56 100755
Binary files a/android/src/main/jniLibs/x86/libpubkymobile.so and b/android/src/main/jniLibs/x86/libpubkymobile.so differ
diff --git a/android/src/main/jniLibs/x86_64/libpubkymobile.so b/android/src/main/jniLibs/x86_64/libpubkymobile.so
index df6893e..fb2bafc 100755
Binary files a/android/src/main/jniLibs/x86_64/libpubkymobile.so and b/android/src/main/jniLibs/x86_64/libpubkymobile.so differ
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index fdb1fe6..134024d 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -1237,7 +1237,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - react-native-pubky (0.1.0):
+ - react-native-pubky (0.2.0):
- DoubleConversion
- glog
- hermes-engine
@@ -1757,7 +1757,7 @@ SPEC CHECKSUMS:
React-logger: 4072f39df335ca443932e0ccece41fbeb5ca8404
React-Mapbuffer: 714f2fae68edcabfc332b754e9fbaa8cfc68fdd4
React-microtasksnativemodule: 4943ad8f99be8ccf5a63329fa7d269816609df9e
- react-native-pubky: 10845f4997bf81181a4f915d22642d04492daec7
+ react-native-pubky: 9fb377be57b3f0ee6a91aca7e2e6a754a756f11b
React-nativeconfig: 4a9543185905fe41014c06776bf126083795aed9
React-NativeModulesApple: 0506da59fc40d2e1e6e12a233db5e81c46face27
React-perflogger: 3bbb82f18e9ac29a1a6931568e99d6305ef4403b
diff --git a/example/src/App.tsx b/example/src/App.tsx
index 16f55c3..1a91ceb 100644
--- a/example/src/App.tsx
+++ b/example/src/App.tsx
@@ -1,33 +1,9 @@
import { StyleSheet, View, Button } from 'react-native';
-import { auth } from '@synonymdev/react-native-pubky';
-import { getAddress } from 'react-native-address-generator';
-
+import { auth, parseAuthUrl } from '@synonymdev/react-native-pubky';
export default function App() {
return (
-
);
}
diff --git a/ios/Frameworks/PubkyMobile.xcframework/ios-arm64-simulator/Headers/pubkymobileFFI.h b/ios/Frameworks/PubkyMobile.xcframework/ios-arm64-simulator/Headers/pubkymobileFFI.h
index e14d3dc..669671c 100644
--- a/ios/Frameworks/PubkyMobile.xcframework/ios-arm64-simulator/Headers/pubkymobileFFI.h
+++ b/ios/Frameworks/PubkyMobile.xcframework/ios-arm64-simulator/Headers/pubkymobileFFI.h
@@ -65,6 +65,8 @@ typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
// Scaffolding functions
void* _Nonnull uniffi_pubkymobile_fn_func_auth(RustBuffer url, RustBuffer secret_key
);
+RustBuffer uniffi_pubkymobile_fn_func_parse_auth_url(RustBuffer url, RustCallStatus *_Nonnull out_status
+);
RustBuffer ffi_pubkymobile_rustbuffer_alloc(int32_t size, RustCallStatus *_Nonnull out_status
);
RustBuffer ffi_pubkymobile_rustbuffer_from_bytes(ForeignBytes bytes, RustCallStatus *_Nonnull out_status
@@ -181,6 +183,9 @@ void ffi_pubkymobile_rust_future_complete_void(void* _Nonnull handle, RustCallSt
);
uint16_t uniffi_pubkymobile_checksum_func_auth(void
+);
+uint16_t uniffi_pubkymobile_checksum_func_parse_auth_url(void
+
);
uint32_t ffi_pubkymobile_uniffi_contract_version(void
diff --git a/ios/Frameworks/PubkyMobile.xcframework/ios-arm64-simulator/libpubkymobile.a b/ios/Frameworks/PubkyMobile.xcframework/ios-arm64-simulator/libpubkymobile.a
index 6d34aa4..c3f3dc4 100644
Binary files a/ios/Frameworks/PubkyMobile.xcframework/ios-arm64-simulator/libpubkymobile.a and b/ios/Frameworks/PubkyMobile.xcframework/ios-arm64-simulator/libpubkymobile.a differ
diff --git a/ios/Frameworks/PubkyMobile.xcframework/ios-arm64/Headers/pubkymobileFFI.h b/ios/Frameworks/PubkyMobile.xcframework/ios-arm64/Headers/pubkymobileFFI.h
index e14d3dc..669671c 100644
--- a/ios/Frameworks/PubkyMobile.xcframework/ios-arm64/Headers/pubkymobileFFI.h
+++ b/ios/Frameworks/PubkyMobile.xcframework/ios-arm64/Headers/pubkymobileFFI.h
@@ -65,6 +65,8 @@ typedef void (*UniFfiRustFutureContinuation)(void * _Nonnull, int8_t);
// Scaffolding functions
void* _Nonnull uniffi_pubkymobile_fn_func_auth(RustBuffer url, RustBuffer secret_key
);
+RustBuffer uniffi_pubkymobile_fn_func_parse_auth_url(RustBuffer url, RustCallStatus *_Nonnull out_status
+);
RustBuffer ffi_pubkymobile_rustbuffer_alloc(int32_t size, RustCallStatus *_Nonnull out_status
);
RustBuffer ffi_pubkymobile_rustbuffer_from_bytes(ForeignBytes bytes, RustCallStatus *_Nonnull out_status
@@ -181,6 +183,9 @@ void ffi_pubkymobile_rust_future_complete_void(void* _Nonnull handle, RustCallSt
);
uint16_t uniffi_pubkymobile_checksum_func_auth(void
+);
+uint16_t uniffi_pubkymobile_checksum_func_parse_auth_url(void
+
);
uint32_t ffi_pubkymobile_uniffi_contract_version(void
diff --git a/ios/Frameworks/PubkyMobile.xcframework/ios-arm64/libpubkymobile.a b/ios/Frameworks/PubkyMobile.xcframework/ios-arm64/libpubkymobile.a
index 3316626..3eb1b1b 100644
Binary files a/ios/Frameworks/PubkyMobile.xcframework/ios-arm64/libpubkymobile.a and b/ios/Frameworks/PubkyMobile.xcframework/ios-arm64/libpubkymobile.a differ
diff --git a/ios/Pubky.mm b/ios/Pubky.mm
index 59b0c8e..7b2cfc4 100644
--- a/ios/Pubky.mm
+++ b/ios/Pubky.mm
@@ -7,6 +7,10 @@ RCT_EXTERN_METHOD(auth:(NSString *)url
withResolver:(RCTPromiseResolveBlock)resolve
withRejecter:(RCTPromiseRejectBlock)reject)
+RCT_EXTERN_METHOD(parseAuthUrl:(NSString *)url
+ withResolver:(RCTPromiseResolveBlock)resolve
+ withRejecter:(RCTPromiseRejectBlock)reject)
+
+ (BOOL)requiresMainQueueSetup
{
return NO;
diff --git a/ios/Pubky.swift b/ios/Pubky.swift
index bc399e6..5f54958 100644
--- a/ios/Pubky.swift
+++ b/ios/Pubky.swift
@@ -2,15 +2,26 @@ import Foundation
@objc(Pubky)
class Pubky: NSObject {
- @objc(auth:secretKey:withResolver:withRejecter:)
- func auth(_ url: String, secretKey: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
- Task {
- do {
- let result = try await react_native_pubky.auth(url: url, secretKey: secretKey)
- resolve(result)
- } catch {
- reject("auth Error", "Failed to auth", error)
- }
- }
- }
+ @objc(auth:secretKey:withResolver:withRejecter:)
+ func auth(_ url: String, secretKey: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
+ Task {
+ do {
+ let result = try await react_native_pubky.auth(url: url, secretKey: secretKey)
+ resolve(result)
+ } catch {
+ reject("auth Error", "Failed to auth", error)
+ }
+ }
+ }
+ @objc(parseAuthUrl:withResolver:withRejecter:)
+ func parseAuthUrl(_ url: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
+ Task {
+ do {
+ let result = react_native_pubky.parseAuthUrl(url: url)
+ resolve(result)
+ } catch {
+ reject("parseAuthUrl Error", "Failed to parse auth url", error)
+ }
+ }
+ }
}
diff --git a/ios/pubkymobile.swift b/ios/pubkymobile.swift
index 7ac7292..106c1d3 100644
--- a/ios/pubkymobile.swift
+++ b/ios/pubkymobile.swift
@@ -438,6 +438,15 @@ public func auth(url: String, secretKey: String) async -> [String] {
+public func parseAuthUrl(url: String) -> [String] {
+ return try! FfiConverterSequenceString.lift(
+ try! rustCall() {
+ uniffi_pubkymobile_fn_func_parse_auth_url(
+ FfiConverterString.lower(url),$0)
+}
+ )
+}
+
private enum InitializationResult {
case ok
case contractVersionMismatch
@@ -456,6 +465,9 @@ private var initializationResult: InitializationResult {
if (uniffi_pubkymobile_checksum_func_auth() != 46918) {
return InitializationResult.apiChecksumMismatch
}
+ if (uniffi_pubkymobile_checksum_func_parse_auth_url() != 29088) {
+ return InitializationResult.apiChecksumMismatch
+ }
uniffiInitContinuationCallback()
return InitializationResult.ok
diff --git a/package.json b/package.json
index 2584a65..c2811c3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@synonymdev/react-native-pubky",
- "version": "0.1.1",
+ "version": "0.2.0",
"description": "React Native Implementation of Pubky",
"source": "./src/index.tsx",
"main": "./lib/commonjs/index.js",
@@ -49,7 +49,8 @@
"cargo-build": "cd rust && cargo build && cd pubky && cargo build && cd pubky && cargo build && cd ../ && cd pubky-common && cargo build && cd ../ && cd pubky-homeserver && cargo build && cd ../../../",
"update-bindings:ios": "npm run cargo-build && node setup-ios-bindings.js && npm run reinstall",
"update-bindings:android": "npm run cargo-build && node setup-android-bindings.js && npm run reinstall",
- "update-bindings": "npm run reinstall && npm run cargo-build && npm run update-bindings:ios && npm run update-bindings:android"
+ "update-bindings": "npm run reinstall && npm run cargo-build && npm run update-bindings:ios && npm run update-bindings:android",
+ "rebuild": "rm -rf node_modules && cd example && rm -rf node_modules && cd ios && rm -rf Pods Podfile.lock build && cd ../../ && npm run cargo-build && yarn install && npm run update-bindings && cd example && yarn install && bundle install && cd ios && pod install && cd ../ && yarn build:ios && yarn ios"
},
"keywords": [
"pubky",
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index 258f6c3..da9e546 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -1,6 +1,9 @@
uniffi::setup_scaffolding!();
+
+use std::collections::HashMap;
use pubky::PubkyClient;
use hex;
+use serde::Serialize;
use url::Url;
#[uniffi::export]
@@ -31,6 +34,85 @@ async fn auth(url: String, secret_key: String) -> Vec {
}
}
+#[uniffi::export]
+fn parse_auth_url(url: String) -> Vec {
+ let parsed_details = match parse_pubky_auth_url(&url) {
+ Ok(details) => details,
+ Err(error) => return create_response_vector(true, error),
+ };
+ match pubky_auth_details_to_json(&parsed_details) {
+ Ok(json) => create_response_vector(false, json),
+ Err(error) => create_response_vector(true, error),
+ }
+}
+
+#[derive(Debug, Serialize)]
+struct Capability {
+ path: String,
+ permission: String,
+}
+
+#[derive(Debug, Serialize)]
+struct PubkyAuthDetails {
+ relay: String,
+ capabilities: Vec,
+ secret: String,
+}
+
+fn pubky_auth_details_to_json(details: &PubkyAuthDetails) -> Result {
+ serde_json::to_string(details).map_err(|_| "Error serializing to JSON".to_string())
+}
+
+fn parse_pubky_auth_url(url_str: &str) -> Result {
+ let url = Url::parse(url_str).map_err(|_| "Invalid URL".to_string())?;
+
+ if url.scheme() != "pubkyauth" {
+ return Err("Invalid scheme, expected 'pubkyauth'".to_string());
+ }
+
+ // Collect query pairs into a HashMap for efficient access
+ let query_params: HashMap<_, _> = url.query_pairs().into_owned().collect();
+
+ let relay = query_params
+ .get("relay")
+ .cloned()
+ .ok_or_else(|| "Missing relay".to_string())?;
+
+ let capabilities_str = query_params
+ .get("capabilities")
+ .cloned()
+ .ok_or_else(|| "Missing capabilities".to_string())?;
+
+ let secret = query_params
+ .get("secret")
+ .cloned()
+ .ok_or_else(|| "Missing secret".to_string())?;
+
+ // Parse capabilities
+ let capabilities = capabilities_str
+ .split(',')
+ .map(|capability| {
+ let mut parts = capability.splitn(2, ':');
+ let path = parts
+ .next()
+ .ok_or_else(|| format!("Invalid capability format in '{}'", capability))?;
+ let permission = parts
+ .next()
+ .ok_or_else(|| format!("Invalid capability format in '{}'", capability))?;
+ Ok(Capability {
+ path: path.to_string(),
+ permission: permission.to_string(),
+ })
+ })
+ .collect::, String>>()?;
+
+ Ok(PubkyAuthDetails {
+ relay,
+ capabilities,
+ secret,
+ })
+}
+
fn create_response_vector(error: bool, data: String) -> Vec {
if error {
vec!["error".to_string(), data]
diff --git a/src/index.tsx b/src/index.tsx
index c092e7d..20b9179 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -28,3 +28,29 @@ export async function auth(
}
return ok(res[1]);
}
+
+type Capability = {
+ path: string;
+ permission: string;
+};
+
+type PubkyAuthDetails = {
+ relay: string;
+ capabilities: Capability[];
+ secret: string;
+};
+
+export async function parseAuthUrl(
+ url: string
+): Promise> {
+ try {
+ const res = await Pubky.parseAuthUrl(url);
+ if (res[0] === 'error') {
+ return err(res[1]);
+ }
+ const parsed = JSON.parse(res[1]);
+ return ok(parsed);
+ } catch (e) {
+ return err(JSON.stringify(e));
+ }
+}