Enhanced features for new rust libraries
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import { NativeEventEmitter, NativeModules } from 'react-native';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { LIBP2P_CONFIG } from '../utils/constants';
|
||||
import type { ConnectionStatusEvent, PeerDiscoveredEvent, PeerInfoEvent } from '../utils/types';
|
||||
import type {
|
||||
@@ -74,8 +75,13 @@ export class Libp2pComponent implements ILibp2pComponent {
|
||||
private _peerId?: PeerId;
|
||||
private _multiaddrs: Multiaddr[] = [];
|
||||
private _started: boolean = false;
|
||||
private _starting: boolean = false;
|
||||
private _error: string | null = null;
|
||||
private cachedConnections: Connection[] = [];
|
||||
private options: Libp2pOptions;
|
||||
private nodeStartedListener: any = null;
|
||||
private nodeStoppedListener: any = null;
|
||||
private errorListener: any = null;
|
||||
|
||||
constructor(options?: Libp2pOptions, nativeModules?: typeof NativeModules) {
|
||||
// Allow dependency injection of NativeModules for testing
|
||||
@@ -90,6 +96,7 @@ export class Libp2pComponent implements ILibp2pComponent {
|
||||
|
||||
this.setupNativeEventListeners();
|
||||
this.setupProtocolHandlers();
|
||||
this.setupStateListeners();
|
||||
}
|
||||
|
||||
get peerId(): PeerId | null {
|
||||
@@ -100,6 +107,14 @@ export class Libp2pComponent implements ILibp2pComponent {
|
||||
return this._multiaddrs;
|
||||
}
|
||||
|
||||
get isStarting(): boolean {
|
||||
return this._starting;
|
||||
}
|
||||
|
||||
get error(): string | null {
|
||||
return this._error;
|
||||
}
|
||||
|
||||
private setupNativeEventListeners(): void {
|
||||
// Map native events to js-libp2p style events
|
||||
|
||||
@@ -195,6 +210,27 @@ export class Libp2pComponent implements ILibp2pComponent {
|
||||
);
|
||||
}
|
||||
|
||||
private setupStateListeners(): void {
|
||||
// Listen for node started event
|
||||
this.nodeStartedListener = this.eventEmitter.addListener('onNodeStarted', () => {
|
||||
this._started = true;
|
||||
this._starting = false;
|
||||
this._error = null;
|
||||
});
|
||||
|
||||
// Listen for node stopped event
|
||||
this.nodeStoppedListener = this.eventEmitter.addListener('onNodeStopped', () => {
|
||||
this._started = false;
|
||||
this._starting = false;
|
||||
});
|
||||
|
||||
// Listen for error events
|
||||
this.errorListener = this.eventEmitter.addListener('onError', (event: any) => {
|
||||
this._error = event.error || 'Unknown error';
|
||||
this._starting = false;
|
||||
});
|
||||
}
|
||||
|
||||
private emit<K extends keyof Libp2pEvents>(event: K, detail: unknown): void {
|
||||
const handlers = this.eventHandlers.get(event);
|
||||
if (handlers) {
|
||||
@@ -206,6 +242,14 @@ export class Libp2pComponent implements ILibp2pComponent {
|
||||
}
|
||||
|
||||
async start(): Promise<void> {
|
||||
if (this._started || this._starting) {
|
||||
return; // Already started or starting
|
||||
}
|
||||
|
||||
this._starting = true;
|
||||
this._error = null;
|
||||
|
||||
try {
|
||||
// Register protocols if any
|
||||
if (this.options.protocols && this.nativeModule.registerProtocolHandler) {
|
||||
for (const protocol of this.options.protocols) {
|
||||
@@ -213,25 +257,65 @@ export class Libp2pComponent implements ILibp2pComponent {
|
||||
}
|
||||
}
|
||||
|
||||
// Load stored private key if available and not provided
|
||||
let privateKeyBytes = this.options.keypair?.privateKey;
|
||||
if (!privateKeyBytes) {
|
||||
try {
|
||||
const storedKey = await AsyncStorage.getItem('libp2p_private_key');
|
||||
if (storedKey) {
|
||||
privateKeyBytes = new Uint8Array(JSON.parse(storedKey));
|
||||
}
|
||||
} catch (err) {
|
||||
// Failed to load stored key, will generate new one
|
||||
}
|
||||
}
|
||||
|
||||
// Pass configuration options to native module including keypair
|
||||
const config = {
|
||||
const config: any = {
|
||||
tcpPort: this.options.config?.tcpPort,
|
||||
wsPort: this.options.config?.wsPort,
|
||||
// Convert Uint8Array to base64 for passing to native module
|
||||
// Using Buffer.from for proper encoding of binary data
|
||||
privateKey: this.options.keypair?.privateKey
|
||||
? Buffer.from(this.options.keypair.privateKey).toString('base64')
|
||||
: undefined,
|
||||
};
|
||||
|
||||
await this.nativeModule.startLibp2p(config);
|
||||
if (privateKeyBytes) {
|
||||
// Convert Uint8Array to base64 for passing to native module
|
||||
config.privateKey = Buffer.from(privateKeyBytes).toString('base64');
|
||||
}
|
||||
|
||||
const result = await this.nativeModule.startLibp2p(config);
|
||||
|
||||
// Update internal state from result
|
||||
if (result.peerId) {
|
||||
this._peerId = new SimplePeerId(result.peerId);
|
||||
}
|
||||
if (result.multiaddrs) {
|
||||
this._multiaddrs = result.multiaddrs.map((addr: string) => new SimpleMultiaddr(addr));
|
||||
}
|
||||
|
||||
// Store the private key if we generated a new one
|
||||
if (result.privateKey && !privateKeyBytes) {
|
||||
try {
|
||||
const keyBytes = Buffer.from(result.privateKey, 'base64');
|
||||
await AsyncStorage.setItem('libp2p_private_key', JSON.stringify(Array.from(keyBytes)));
|
||||
} catch (err) {
|
||||
// Failed to store key, but continue
|
||||
}
|
||||
}
|
||||
|
||||
this._started = true;
|
||||
this._starting = false;
|
||||
} catch (error) {
|
||||
this._starting = false;
|
||||
this._error = error instanceof Error ? error.message : 'Failed to start';
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async stop(): Promise<void> {
|
||||
try {
|
||||
await this.nativeModule.stopLibp2p();
|
||||
this._started = false;
|
||||
this._peerId = undefined;
|
||||
this._multiaddrs = [];
|
||||
} catch (error) {
|
||||
// If libp2p wasn't running, that's okay - just update our state
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
@@ -245,6 +329,24 @@ export class Libp2pComponent implements ILibp2pComponent {
|
||||
}
|
||||
}
|
||||
|
||||
cleanup(): void {
|
||||
// Remove state listeners
|
||||
if (this.nodeStartedListener) {
|
||||
this.nodeStartedListener.remove();
|
||||
this.nodeStartedListener = null;
|
||||
}
|
||||
if (this.nodeStoppedListener) {
|
||||
this.nodeStoppedListener.remove();
|
||||
this.nodeStoppedListener = null;
|
||||
}
|
||||
if (this.errorListener) {
|
||||
this.errorListener.remove();
|
||||
this.errorListener = null;
|
||||
}
|
||||
// Clear event handlers
|
||||
this.eventHandlers.clear();
|
||||
}
|
||||
|
||||
async dial(multiaddr: string): Promise<Connection> {
|
||||
await this.nativeModule.connectToPeer(multiaddr);
|
||||
|
||||
@@ -368,6 +470,20 @@ export class Libp2pComponent implements ILibp2pComponent {
|
||||
throw new Error('Protocol sending not supported on this platform');
|
||||
}
|
||||
}
|
||||
|
||||
async acceptConnection(peerId: string): Promise<void> {
|
||||
if (!this.nativeModule.acceptConnection) {
|
||||
throw new Error('acceptConnection not supported by native module');
|
||||
}
|
||||
await this.nativeModule.acceptConnection(peerId);
|
||||
}
|
||||
|
||||
async rejectConnection(peerId: string): Promise<void> {
|
||||
if (!this.nativeModule.rejectConnection) {
|
||||
throw new Error('rejectConnection not supported by native module');
|
||||
}
|
||||
await this.nativeModule.rejectConnection(peerId);
|
||||
}
|
||||
}
|
||||
|
||||
// Type definition for CustomEventInit
|
||||
|
||||
@@ -43,6 +43,9 @@ export interface ProtocolHandler {
|
||||
export interface ILibp2pComponent {
|
||||
peerId: PeerId | null;
|
||||
multiaddrs: Multiaddr[];
|
||||
isStarted: boolean;
|
||||
isStarting: boolean;
|
||||
error: string | null;
|
||||
|
||||
start(): Promise<void>;
|
||||
stop(): Promise<void>;
|
||||
@@ -52,6 +55,9 @@ export interface ILibp2pComponent {
|
||||
sendProtocolData(peerId: string, protocolId: string, data: Uint8Array): Promise<void>;
|
||||
refreshDiscovery(): Promise<void>;
|
||||
pingPeer(peerId: string): Promise<{ success: boolean; rtt?: number; peerId: string }>;
|
||||
acceptConnection(peerId: string): Promise<void>;
|
||||
rejectConnection(peerId: string): Promise<void>;
|
||||
cleanup(): void;
|
||||
|
||||
addEventListener<K extends keyof Libp2pEvents>(
|
||||
event: K,
|
||||
|
||||
Reference in New Issue
Block a user