Initial commit: libp2p-native-bridge package
- Extracted libp2p component from main app - Created modular package structure with interfaces and implementations - Added dependency injection for NativeModules - Configured for IOR loading from Gitea - Added comprehensive README and documentation
This commit is contained in:
298
implementations/SettingsService.ts
Normal file
298
implementations/SettingsService.ts
Normal file
@@ -0,0 +1,298 @@
|
||||
/**
|
||||
* Settings Service
|
||||
*
|
||||
* Manages application settings and provides methods to:
|
||||
* - Load/save settings
|
||||
* - Apply settings to native modules
|
||||
* - Reset application data
|
||||
*/
|
||||
|
||||
import { LoggerComponent } from 'ior:gitea:gitea.metatrom.net:universal-components/logger@1.0.0';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { NativeModules, Platform } from 'react-native';
|
||||
|
||||
// Storage keys
|
||||
const SETTINGS_KEY = '@libp2p_settings';
|
||||
|
||||
// Default settings
|
||||
export const DEFAULT_SETTINGS = {
|
||||
tcpPort: 10000,
|
||||
wsPort: 10005,
|
||||
discoveryTimeout: 30000,
|
||||
enableDebugLogs: false,
|
||||
autoDiscovery: false,
|
||||
useCustomPorts: false,
|
||||
dhtServerUrl: 'ws://192.168.188.40:3000/ws', // This is the default/fallback DHT server URL
|
||||
};
|
||||
|
||||
export interface AppSettings {
|
||||
tcpPort: number;
|
||||
wsPort: number;
|
||||
discoveryTimeout: number;
|
||||
enableDebugLogs: boolean;
|
||||
autoDiscovery: boolean;
|
||||
useCustomPorts: boolean;
|
||||
dhtServerUrl: string;
|
||||
}
|
||||
|
||||
export interface INativeModules {
|
||||
Libp2pModule?: any;
|
||||
DiscoveryModule?: any;
|
||||
SecureStorageModule?: any;
|
||||
}
|
||||
|
||||
export class SettingsService {
|
||||
private static instance: SettingsService | null = null;
|
||||
private currentSettings: AppSettings = DEFAULT_SETTINGS;
|
||||
private logger = new LoggerComponent('SettingsService');
|
||||
private nativeModules: INativeModules;
|
||||
|
||||
private constructor(nativeModules?: INativeModules) {
|
||||
// Allow dependency injection of native modules
|
||||
this.nativeModules = nativeModules || NativeModules;
|
||||
}
|
||||
|
||||
public static getInstance(nativeModules?: INativeModules): SettingsService {
|
||||
if (!SettingsService.instance) {
|
||||
SettingsService.instance = new SettingsService(nativeModules);
|
||||
}
|
||||
return SettingsService.instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the singleton instance (useful for testing)
|
||||
*/
|
||||
public static resetInstance(): void {
|
||||
SettingsService.instance = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize settings service
|
||||
*/
|
||||
async initialize(): Promise<AppSettings> {
|
||||
try {
|
||||
const settings = await this.loadSettings();
|
||||
this.currentSettings = settings;
|
||||
return settings;
|
||||
} catch (error) {
|
||||
this.logger.error('[SettingsService] Failed to initialize:', error);
|
||||
return DEFAULT_SETTINGS;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load settings from storage
|
||||
*/
|
||||
async loadSettings(): Promise<AppSettings> {
|
||||
try {
|
||||
const stored = await AsyncStorage.getItem(SETTINGS_KEY);
|
||||
if (stored) {
|
||||
const parsed = JSON.parse(stored);
|
||||
return { ...DEFAULT_SETTINGS, ...parsed };
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error('[SettingsService] Failed to load settings:', error);
|
||||
}
|
||||
return DEFAULT_SETTINGS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings to storage
|
||||
*/
|
||||
async saveSettings(settings: AppSettings): Promise<void> {
|
||||
try {
|
||||
await AsyncStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
|
||||
this.currentSettings = settings;
|
||||
|
||||
// Apply settings to native modules if needed
|
||||
await this.applySettings(settings);
|
||||
} catch (error) {
|
||||
this.logger.error('[SettingsService] Failed to save settings:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply settings to native modules
|
||||
*/
|
||||
private async applySettings(settings: AppSettings): Promise<void> {
|
||||
const { Libp2pModule, DiscoveryModule } = this.nativeModules;
|
||||
|
||||
// Apply port settings if custom ports are enabled
|
||||
if (settings.useCustomPorts) {
|
||||
// This would need to be passed to the native modules
|
||||
// when starting the libp2p node
|
||||
this.logger.debug('[SettingsService] Custom ports:', {
|
||||
tcp: settings.tcpPort,
|
||||
ws: settings.wsPort,
|
||||
});
|
||||
}
|
||||
|
||||
// Apply debug logging
|
||||
if (Libp2pModule?.setDebugConsoleOutput) {
|
||||
Libp2pModule.setDebugConsoleOutput(settings.enableDebugLogs);
|
||||
}
|
||||
|
||||
// Apply discovery timeout
|
||||
if (DiscoveryModule?.setDiscoveryTimeout) {
|
||||
DiscoveryModule.setDiscoveryTimeout(settings.discoveryTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current settings
|
||||
*/
|
||||
getSettings(): AppSettings {
|
||||
return this.currentSettings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get specific setting value
|
||||
*/
|
||||
getSetting<K extends keyof AppSettings>(key: K): AppSettings[K] {
|
||||
return this.currentSettings[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Update specific setting
|
||||
*/
|
||||
async updateSetting<K extends keyof AppSettings>(key: K, value: AppSettings[K]): Promise<void> {
|
||||
const newSettings = { ...this.currentSettings, [key]: value };
|
||||
await this.saveSettings(newSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset all application data
|
||||
*/
|
||||
async resetAllData(): Promise<void> {
|
||||
try {
|
||||
this.logger.info('[SettingsService] Starting app reset...');
|
||||
|
||||
// Clear all AsyncStorage (this includes dht_discovered_users)
|
||||
const allKeys = await AsyncStorage.getAllKeys();
|
||||
this.logger.debug('[SettingsService] Clearing AsyncStorage keys:', allKeys);
|
||||
await AsyncStorage.multiRemove(allKeys);
|
||||
|
||||
// Clear native module data
|
||||
await this.clearNativeData();
|
||||
|
||||
// Reset settings to defaults
|
||||
this.currentSettings = DEFAULT_SETTINGS;
|
||||
|
||||
this.logger.info('[SettingsService] App reset complete');
|
||||
} catch (error) {
|
||||
this.logger.error('[SettingsService] Failed to reset app data:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear native module stored data
|
||||
*/
|
||||
private async clearNativeData(): Promise<void> {
|
||||
const { Libp2pModule, DiscoveryModule, SecureStorageModule } = this.nativeModules;
|
||||
|
||||
try {
|
||||
if (Platform.OS === 'ios') {
|
||||
// Clear iOS UserDefaults
|
||||
if (Libp2pModule?.clearStoredData) {
|
||||
await Libp2pModule.clearStoredData();
|
||||
}
|
||||
if (DiscoveryModule?.clearStoredData) {
|
||||
await DiscoveryModule.clearStoredData();
|
||||
}
|
||||
if (SecureStorageModule?.clearAll) {
|
||||
await SecureStorageModule.clearAll();
|
||||
}
|
||||
} else if (Platform.OS === 'android') {
|
||||
// Clear Android SharedPreferences
|
||||
if (Libp2pModule?.clearStoredData) {
|
||||
await Libp2pModule.clearStoredData();
|
||||
}
|
||||
if (DiscoveryModule?.clearStoredData) {
|
||||
await DiscoveryModule.clearStoredData();
|
||||
}
|
||||
if (SecureStorageModule?.clearAll) {
|
||||
await SecureStorageModule.clearAll();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error('[SettingsService] Failed to clear native data:', error);
|
||||
// Continue even if native clear fails
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Export all app data for debugging
|
||||
*/
|
||||
async exportDebugData(): Promise<object> {
|
||||
try {
|
||||
const allKeys = await AsyncStorage.getAllKeys();
|
||||
const allData: Record<string, unknown> = {};
|
||||
|
||||
for (const key of allKeys) {
|
||||
try {
|
||||
const value = await AsyncStorage.getItem(key);
|
||||
allData[key] = value ? JSON.parse(value) : null;
|
||||
} catch {
|
||||
// If JSON parse fails, store as string
|
||||
allData[key] = await AsyncStorage.getItem(key);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
settings: this.currentSettings,
|
||||
storedData: allData,
|
||||
platform: Platform.OS,
|
||||
timestamp: new Date().toISOString(),
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('[SettingsService] Failed to export debug data:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is first app launch
|
||||
*/
|
||||
async isFirstLaunch(): Promise<boolean> {
|
||||
try {
|
||||
const hasSettings = await AsyncStorage.getItem(SETTINGS_KEY);
|
||||
return !hasSettings;
|
||||
} catch {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage info
|
||||
*/
|
||||
async getStorageInfo(): Promise<{
|
||||
keys: string[];
|
||||
totalSize: number;
|
||||
}> {
|
||||
try {
|
||||
const allKeys = await AsyncStorage.getAllKeys();
|
||||
let totalSize = 0;
|
||||
|
||||
for (const key of allKeys) {
|
||||
const value = await AsyncStorage.getItem(key);
|
||||
if (value) {
|
||||
totalSize += value.length;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
keys: [...allKeys], // Convert readonly array to mutable array
|
||||
totalSize,
|
||||
};
|
||||
} catch (error) {
|
||||
this.logger.error('[SettingsService] Failed to get storage info:', error);
|
||||
return { keys: [], totalSize: 0 };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton getter function instead of instance
|
||||
export const getSettingsService = (nativeModules?: INativeModules) =>
|
||||
SettingsService.getInstance(nativeModules);
|
||||
Reference in New Issue
Block a user