- 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
298 lines
8.1 KiB
TypeScript
298 lines
8.1 KiB
TypeScript
/**
|
|
* 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); |