mirror of
https://github.com/DocNR/POWR.git
synced 2025-04-23 01:01:27 +00:00
nostr testing component (programs) functioning with all event kinds
This commit is contained in:
parent
8e85ee4704
commit
f870f2a0ca
64
CHANGELOG.md
64
CHANGELOG.md
@ -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
78
lib/initNDK.ts
Normal 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 };
|
||||
}
|
@ -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);
|
||||
|
||||
|
@ -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');
|
||||
|
Loading…
x
Reference in New Issue
Block a user