mirror of
https://github.com/aljazceru/react-native-pubky.git
synced 2025-12-17 14:44:26 +01:00
feat: implement parseAuthUrl
Adds & Implements parseAuthUrl. Updates README.md. Bumps version to 0.2.0.
This commit is contained in:
14
README.md
14
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);
|
||||
```
|
||||
### <a name="parseAuthUrl"></a>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
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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<String> {
|
||||
)
|
||||
}
|
||||
|
||||
fun `parseAuthUrl`(`url`: String): List<String> {
|
||||
return FfiConverterSequenceString.lift(
|
||||
rustCall() { _status ->
|
||||
_UniFFILib.INSTANCE.uniffi_pubkymobile_fn_func_parse_auth_url(FfiConverterString.lower(`url`),_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.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
|
||||
|
||||
@@ -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 (
|
||||
<View style={styles.container}>
|
||||
<Button
|
||||
title={'getAddress'}
|
||||
onPress={async (): Promise<void> => {
|
||||
const mnemonic =
|
||||
'lazy rally chat way pet outside flame cup oval absurd innocent balcony';
|
||||
const passphrase = 'passphrase';
|
||||
const path = "m/84'/1'/0'/0/0";
|
||||
const network = 'testnet';
|
||||
|
||||
const getAddressRes = await getAddress({
|
||||
mnemonic,
|
||||
path,
|
||||
network,
|
||||
passphrase,
|
||||
});
|
||||
if (getAddressRes.isErr()) {
|
||||
console.log(getAddressRes.error.message);
|
||||
return;
|
||||
}
|
||||
console.log(getAddressRes.value);
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title={'auth'}
|
||||
onPress={async (): Promise<void> => {
|
||||
@@ -46,6 +22,23 @@ export default function App() {
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
title={'parseAuthUrl'}
|
||||
onPress={async (): Promise<void> => {
|
||||
try {
|
||||
const res = await parseAuthUrl(
|
||||
'pubkyauth:///?relay=https://demo.httprelay.io/link&capabilities=/pub/pubky.app:rw,/pub/example.com/nested:rw&secret=FyzJ3gJ1W7boyFZC1Do9fYrRmDNgCLNRwEu_gaBgPUA'
|
||||
);
|
||||
if (res.isErr()) {
|
||||
console.log(res.error.message);
|
||||
return;
|
||||
}
|
||||
console.log(res.value);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Binary file not shown.
@@ -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
|
||||
|
||||
|
||||
Binary file not shown.
@@ -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;
|
||||
|
||||
@@ -13,4 +13,15 @@ class Pubky: NSObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
@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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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<String> {
|
||||
}
|
||||
}
|
||||
|
||||
#[uniffi::export]
|
||||
fn parse_auth_url(url: String) -> Vec<String> {
|
||||
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<Capability>,
|
||||
secret: String,
|
||||
}
|
||||
|
||||
fn pubky_auth_details_to_json(details: &PubkyAuthDetails) -> Result<String, String> {
|
||||
serde_json::to_string(details).map_err(|_| "Error serializing to JSON".to_string())
|
||||
}
|
||||
|
||||
fn parse_pubky_auth_url(url_str: &str) -> Result<PubkyAuthDetails, String> {
|
||||
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::<Result<Vec<_>, String>>()?;
|
||||
|
||||
Ok(PubkyAuthDetails {
|
||||
relay,
|
||||
capabilities,
|
||||
secret,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_response_vector(error: bool, data: String) -> Vec<String> {
|
||||
if error {
|
||||
vec!["error".to_string(), data]
|
||||
|
||||
@@ -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<Result<PubkyAuthDetails>> {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user