nostr testing component (programs) functioning with all event kinds

This commit is contained in:
DocNR 2025-03-04 08:07:27 -05:00
parent 8e85ee4704
commit f870f2a0ca
4 changed files with 173 additions and 44 deletions

View File

@ -8,6 +8,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Successful Nostr protocol integration
- Implemented NDK-mobile for React Native compatibility
- Added secure key management with Expo SecureStore
- Created event signing and publishing functionality
- Built relay connection management system
- Implemented event caching for offline support
- Added support for various Nostr event kinds (Text, Exercise, Template, Workout)
- Programs component for testing Nostr functionality
- Created tabbed interface with Database and Nostr sections
- Implemented user authentication flow
- Added event creation with multiple event types
- Built query functionality for retrieving events
- Developed event display with detailed tag inspection
- Added login/logout capabilities with secure key handling
- Enhanced crypto support for React Native environment
- Implemented proper cryptographic polyfills
- Added secure random number generation
- Built robust key management system
- Developed signer implementation for Nostr
- Zustand workout store for state management
- Created comprehensive workout state store with Zustand
- Implemented selectors for efficient state access
- Added workout persistence and recovery
- Built automatic timer management with background support
- Developed minimization and maximization functionality
- Zustand workout store for state management
- Created comprehensive workout state store with Zustand
- Implemented selectors for efficient state access
@ -119,39 +144,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Content rendering issues in bottom sheet components
### Technical Details
1. Database Schema Enforcement:
1. Nostr Integration:
- Implemented @nostr-dev-kit/ndk-mobile package for React Native compatibility
- Created dedicated NDK store using Zustand for state management
- Built secure key storage and retrieval using Expo SecureStore
- Implemented event creation, signing, and publishing workflow
- Added relay connection management with status tracking
- Developed proper error handling for network operations
2. Cryptographic Implementation:
- Integrated react-native-get-random-values for crypto API polyfill
- Implemented NDKMobilePrivateKeySigner for key operations
- Added proper key format handling (hex, nsec)
- Created secure key generation functionality
- Built robust error handling for cryptographic operations
3. Programs Testing Component:
- Developed dual-purpose interface for Database and Nostr testing
- Implemented login system with key generation and secure storage
- Built event creation interface with multiple event kinds
- Added event querying and display functionality
- Created detailed event inspection with tag visualization
- Added relay status monitoring
4. Database Schema Enforcement:
- Added CHECK constraints for equipment types
- Added CHECK constraints for exercise types
- Added CHECK constraints for categories
- Proper handling of foreign key constraints
2. Input Validation:
5. Input Validation:
- Equipment options: bodyweight, barbell, dumbbell, kettlebell, machine, cable, other
- Exercise types: strength, cardio, bodyweight
- Categories: Push, Pull, Legs, Core
- Difficulty levels: beginner, intermediate, advanced
- Movement patterns: push, pull, squat, hinge, carry, rotation
3. Error Handling:
6. Error Handling:
- Added SQLite error type definitions
- Improved error propagation in LibraryService
- Added transaction rollback on constraint violations
4. Database Services:
7. Database Services:
- Added EventCache service for Nostr events
- Improved ExerciseService with transaction awareness
- Added DevSeederService for development data
- Enhanced error handling and logging
5. Workout State Management with Zustand:
8. Workout State Management with Zustand:
- Implemented selector pattern for performance optimization
- Added module-level timer references for background operation
- Created workout persistence with auto-save functionality
- Developed state recovery for crash protection
- Added support for future Nostr integration
- Implemented workout minimization for multi-tasking
6. Template Details UI Architecture:
9. Template Details UI Architecture:
- Implemented MaterialTopTabNavigator for content organization
- Created screen-specific components for each tab
- Developed conditional rendering based on template source

78
lib/initNDK.ts Normal file
View File

@ -0,0 +1,78 @@
// lib/initNDK.ts
import 'react-native-get-random-values'; // This must be the first import
import NDK, { NDKCacheAdapterSqlite } from '@nostr-dev-kit/ndk-mobile';
import * as SecureStore from 'expo-secure-store';
import { NDKMobilePrivateKeySigner } from './mobile-signer';
// Constants for SecureStore
const PRIVATE_KEY_STORAGE_KEY = 'nostr_privkey';
// Default relays
const DEFAULT_RELAYS = [
'wss://powr.duckdns.org',
'wss://relay.damus.io',
'wss://relay.nostr.band',
'wss://nos.lol'
];
export async function initializeNDK() {
console.log('Initializing NDK with mobile adapter...');
// Create a mobile-specific cache adapter with a valid maxSize
// The error shows maxSize must be greater than 0
const cacheAdapter = new NDKCacheAdapterSqlite('powr', 1000); // Use 1000 as maxSize
// Initialize NDK with mobile-specific options
const ndk = new NDK({
cacheAdapter,
explicitRelayUrls: DEFAULT_RELAYS,
enableOutboxModel: true,
clientName: 'powr',
});
// Initialize cache adapter
await cacheAdapter.initialize();
// Connect to relays
await ndk.connect();
// Set up relay status tracking
const relayStatus: Record<string, 'connected' | 'connecting' | 'disconnected' | 'error'> = {};
DEFAULT_RELAYS.forEach(url => {
relayStatus[url] = 'connecting';
const relay = ndk.pool.getRelay(url);
if (relay) {
relay.on('connect', () => {
console.log(`Connected to relay: ${url}`);
relayStatus[url] = 'connected';
});
relay.on('disconnect', () => {
console.log(`Disconnected from relay: ${url}`);
relayStatus[url] = 'disconnected';
});
}
});
// Check for saved private key
try {
const privateKey = await SecureStore.getItemAsync(PRIVATE_KEY_STORAGE_KEY);
if (privateKey) {
console.log('[NDK] Found saved private key, initializing signer');
// Create mobile-specific signer with private key
const signer = new NDKMobilePrivateKeySigner(privateKey);
ndk.signer = signer;
// Log success
console.log('[NDK] Signer initialized successfully');
}
} catch (error) {
console.error('[NDK] Error initializing with saved key:', error);
// Remove invalid key
await SecureStore.deleteItemAsync(PRIVATE_KEY_STORAGE_KEY);
}
return { ndk, relayStatus };
}

View File

@ -1,11 +1,9 @@
// lib/mobile-signer.ts
import '../lib/crypto-polyfill'; // Import crypto polyfill first
import 'react-native-get-random-values'; // First import - most important!
import * as Crypto from 'expo-crypto';
import * as Random from 'expo-random';
import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk-mobile'; // Import from ndk-mobile
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
import * as nostrTools from 'nostr-tools';
import { setupCryptoPolyfill } from './crypto-polyfill';
/**
* A custom signer implementation for React Native
@ -47,24 +45,12 @@ export class NDKMobilePrivateKeySigner extends NDKPrivateKeySigner {
* Generate a new Nostr keypair
* Uses Expo's crypto functions directly instead of relying on polyfills
*/
// Add this to your generateKeyPair function
export function generateKeyPair() {
try {
// Ensure crypto polyfill is set up
if (typeof setupCryptoPolyfill === 'function') {
setupCryptoPolyfill();
}
let privateKeyBytes;
// Try expo-crypto first since expo-random is deprecated
try {
privateKeyBytes = Crypto.getRandomBytes(32);
} catch (e) {
console.warn('expo-crypto failed:', e);
// Fallback to expo-random as last resort
privateKeyBytes = Random.getRandomBytes(32);
}
// Try expo-crypto
privateKeyBytes = Crypto.getRandomBytes(32);
const privateKey = bytesToHex(privateKeyBytes);
@ -72,6 +58,7 @@ export function generateKeyPair() {
const publicKey = nostrTools.getPublicKey(privateKeyBytes);
// Encode keys in bech32 format
// Fixed the parameter types for nsecEncode - it needs Uint8Array not string
const nsec = nostrTools.nip19.nsecEncode(privateKeyBytes);
const npub = nostrTools.nip19.npubEncode(publicKey);

View File

@ -1,12 +1,15 @@
// stores/ndk.ts
import '@/lib/crypto-polyfill'; // Import crypto polyfill first
// lib/stores/ndk.ts
// IMPORTANT: 'react-native-get-random-values' must be the first import to ensure
// proper crypto polyfill application before other libraries are loaded
import 'react-native-get-random-values';
import { Platform } from 'react-native';
import { create } from 'zustand';
import NDK, { NDKFilter, NDKEvent as NDKEventBase } from '@nostr-dev-kit/ndk';
import { NDKUser } from '@nostr-dev-kit/ndk-mobile';
import { NDKEvent } from '@nostr-dev-kit/ndk-mobile';
// Using standard NDK types but importing NDKEvent from ndk-mobile for compatibility
import NDK, { NDKFilter } from '@nostr-dev-kit/ndk';
import { NDKEvent, NDKUser } from '@nostr-dev-kit/ndk-mobile';
import * as SecureStore from 'expo-secure-store';
import * as Crypto from 'expo-crypto';
import { NDKMobilePrivateKeySigner, generateKeyPair } from '@/lib/mobile-signer';
import { setupCryptoPolyfill } from '@/lib/crypto-polyfill';
// Constants for SecureStore
const PRIVATE_KEY_STORAGE_KEY = 'nostr_privkey';
@ -64,6 +67,14 @@ export const useNDKStore = create<NDKStoreState & NDKStoreActions>((set, get) =>
});
set({ relayStatus });
// IMPORTANT: Due to the lack of an Expo config plugin for ndk-mobile,
// we're using a standard NDK initialization approach rather than trying to use
// ndk-mobile's native modules, which require a custom build.
//
// When an Expo plugin becomes available for ndk-mobile, we can remove this
// fallback approach and use the initializeNDK() function directly.
console.log('[NDK] Using standard NDK initialization');
// Initialize NDK with relays
const ndk = new NDK({
explicitRelayUrls: DEFAULT_RELAYS
@ -243,7 +254,15 @@ export const useNDKStore = create<NDKStoreState & NDKStoreActions>((set, get) =>
}
},
// In your publishEvent function in ndk.ts:
// IMPORTANT: This method uses monkey patching to make event signing work
// in React Native environment. This is necessary because the underlying
// Nostr libraries expect Web Crypto API to be available.
//
// When ndk-mobile gets proper Expo support, this function can be simplified to:
// 1. Create the event
// 2. Call event.sign() directly
// 3. Call event.publish()
// without the monkey patching code.
publishEvent: async (kind: number, content: string, tags: string[][]) => {
try {
const { ndk, isAuthenticated, currentUser } = get();
@ -256,21 +275,24 @@ export const useNDKStore = create<NDKStoreState & NDKStoreActions>((set, get) =>
throw new Error('Not authenticated');
}
// Define custom functions we'll use to override crypto
const customRandomBytes = (length: number): Uint8Array => {
console.log('Using custom randomBytes in event signing');
// Use type assertion to avoid TypeScript error
return (Crypto as any).getRandomBytes(length);
};
// Create event
console.log('Creating event...');
const event = new NDKEvent(ndk);
event.kind = kind;
event.content = content;
event.tags = tags;
// Direct monkey-patching approach
// MONKEY PATCHING APPROACH:
// This is needed because the standard NDK doesn't properly work with
// React Native's crypto implementation. When ndk-mobile adds proper Expo
// support, this can be removed.
try {
// Define custom function for random bytes generation
const customRandomBytes = (length: number): Uint8Array => {
console.log('Using custom randomBytes in event signing');
return (Crypto as any).getRandomBytes(length);
};
// Try to find and override the randomBytes function
const nostrTools = require('nostr-tools');
const nobleHashes = require('@noble/hashes/utils');