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]
|
## [Unreleased]
|
||||||
|
|
||||||
### Added
|
### 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
|
- Zustand workout store for state management
|
||||||
- Created comprehensive workout state store with Zustand
|
- Created comprehensive workout state store with Zustand
|
||||||
- Implemented selectors for efficient state access
|
- 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
|
- Content rendering issues in bottom sheet components
|
||||||
|
|
||||||
### Technical Details
|
### 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 equipment types
|
||||||
- Added CHECK constraints for exercise types
|
- Added CHECK constraints for exercise types
|
||||||
- Added CHECK constraints for categories
|
- Added CHECK constraints for categories
|
||||||
- Proper handling of foreign key constraints
|
- Proper handling of foreign key constraints
|
||||||
|
5. Input Validation:
|
||||||
2. Input Validation:
|
|
||||||
- Equipment options: bodyweight, barbell, dumbbell, kettlebell, machine, cable, other
|
- Equipment options: bodyweight, barbell, dumbbell, kettlebell, machine, cable, other
|
||||||
- Exercise types: strength, cardio, bodyweight
|
- Exercise types: strength, cardio, bodyweight
|
||||||
- Categories: Push, Pull, Legs, Core
|
- Categories: Push, Pull, Legs, Core
|
||||||
- Difficulty levels: beginner, intermediate, advanced
|
- Difficulty levels: beginner, intermediate, advanced
|
||||||
- Movement patterns: push, pull, squat, hinge, carry, rotation
|
- Movement patterns: push, pull, squat, hinge, carry, rotation
|
||||||
|
6. Error Handling:
|
||||||
3. Error Handling:
|
|
||||||
- Added SQLite error type definitions
|
- Added SQLite error type definitions
|
||||||
- Improved error propagation in LibraryService
|
- Improved error propagation in LibraryService
|
||||||
- Added transaction rollback on constraint violations
|
- Added transaction rollback on constraint violations
|
||||||
|
7. Database Services:
|
||||||
4. Database Services:
|
|
||||||
- Added EventCache service for Nostr events
|
- Added EventCache service for Nostr events
|
||||||
- Improved ExerciseService with transaction awareness
|
- Improved ExerciseService with transaction awareness
|
||||||
- Added DevSeederService for development data
|
- Added DevSeederService for development data
|
||||||
- Enhanced error handling and logging
|
- Enhanced error handling and logging
|
||||||
|
8. Workout State Management with Zustand:
|
||||||
5. Workout State Management with Zustand:
|
|
||||||
- Implemented selector pattern for performance optimization
|
- Implemented selector pattern for performance optimization
|
||||||
- Added module-level timer references for background operation
|
- Added module-level timer references for background operation
|
||||||
- Created workout persistence with auto-save functionality
|
- Created workout persistence with auto-save functionality
|
||||||
- Developed state recovery for crash protection
|
- Developed state recovery for crash protection
|
||||||
- Added support for future Nostr integration
|
- Added support for future Nostr integration
|
||||||
- Implemented workout minimization for multi-tasking
|
- Implemented workout minimization for multi-tasking
|
||||||
|
9. Template Details UI Architecture:
|
||||||
6. Template Details UI Architecture:
|
|
||||||
- Implemented MaterialTopTabNavigator for content organization
|
- Implemented MaterialTopTabNavigator for content organization
|
||||||
- Created screen-specific components for each tab
|
- Created screen-specific components for each tab
|
||||||
- Developed conditional rendering based on template source
|
- 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
|
// 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 Crypto from 'expo-crypto';
|
||||||
import * as Random from 'expo-random';
|
import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk-mobile'; // Import from ndk-mobile
|
||||||
import { NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
|
|
||||||
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
|
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
|
||||||
import * as nostrTools from 'nostr-tools';
|
import * as nostrTools from 'nostr-tools';
|
||||||
import { setupCryptoPolyfill } from './crypto-polyfill';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A custom signer implementation for React Native
|
* A custom signer implementation for React Native
|
||||||
@ -47,24 +45,12 @@ export class NDKMobilePrivateKeySigner extends NDKPrivateKeySigner {
|
|||||||
* Generate a new Nostr keypair
|
* Generate a new Nostr keypair
|
||||||
* Uses Expo's crypto functions directly instead of relying on polyfills
|
* Uses Expo's crypto functions directly instead of relying on polyfills
|
||||||
*/
|
*/
|
||||||
// Add this to your generateKeyPair function
|
|
||||||
export function generateKeyPair() {
|
export function generateKeyPair() {
|
||||||
try {
|
try {
|
||||||
// Ensure crypto polyfill is set up
|
|
||||||
if (typeof setupCryptoPolyfill === 'function') {
|
|
||||||
setupCryptoPolyfill();
|
|
||||||
}
|
|
||||||
|
|
||||||
let privateKeyBytes;
|
let privateKeyBytes;
|
||||||
|
|
||||||
// Try expo-crypto first since expo-random is deprecated
|
// Try expo-crypto
|
||||||
try {
|
privateKeyBytes = Crypto.getRandomBytes(32);
|
||||||
privateKeyBytes = Crypto.getRandomBytes(32);
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('expo-crypto failed:', e);
|
|
||||||
// Fallback to expo-random as last resort
|
|
||||||
privateKeyBytes = Random.getRandomBytes(32);
|
|
||||||
}
|
|
||||||
|
|
||||||
const privateKey = bytesToHex(privateKeyBytes);
|
const privateKey = bytesToHex(privateKeyBytes);
|
||||||
|
|
||||||
@ -72,6 +58,7 @@ export function generateKeyPair() {
|
|||||||
const publicKey = nostrTools.getPublicKey(privateKeyBytes);
|
const publicKey = nostrTools.getPublicKey(privateKeyBytes);
|
||||||
|
|
||||||
// Encode keys in bech32 format
|
// Encode keys in bech32 format
|
||||||
|
// Fixed the parameter types for nsecEncode - it needs Uint8Array not string
|
||||||
const nsec = nostrTools.nip19.nsecEncode(privateKeyBytes);
|
const nsec = nostrTools.nip19.nsecEncode(privateKeyBytes);
|
||||||
const npub = nostrTools.nip19.npubEncode(publicKey);
|
const npub = nostrTools.nip19.npubEncode(publicKey);
|
||||||
|
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
// stores/ndk.ts
|
// lib/stores/ndk.ts
|
||||||
import '@/lib/crypto-polyfill'; // Import crypto polyfill first
|
// 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 { create } from 'zustand';
|
||||||
import NDK, { NDKFilter, NDKEvent as NDKEventBase } from '@nostr-dev-kit/ndk';
|
// Using standard NDK types but importing NDKEvent from ndk-mobile for compatibility
|
||||||
import { NDKUser } from '@nostr-dev-kit/ndk-mobile';
|
import NDK, { NDKFilter } from '@nostr-dev-kit/ndk';
|
||||||
import { NDKEvent } from '@nostr-dev-kit/ndk-mobile';
|
import { NDKEvent, NDKUser } from '@nostr-dev-kit/ndk-mobile';
|
||||||
import * as SecureStore from 'expo-secure-store';
|
import * as SecureStore from 'expo-secure-store';
|
||||||
|
import * as Crypto from 'expo-crypto';
|
||||||
import { NDKMobilePrivateKeySigner, generateKeyPair } from '@/lib/mobile-signer';
|
import { NDKMobilePrivateKeySigner, generateKeyPair } from '@/lib/mobile-signer';
|
||||||
import { setupCryptoPolyfill } from '@/lib/crypto-polyfill';
|
|
||||||
|
|
||||||
// Constants for SecureStore
|
// Constants for SecureStore
|
||||||
const PRIVATE_KEY_STORAGE_KEY = 'nostr_privkey';
|
const PRIVATE_KEY_STORAGE_KEY = 'nostr_privkey';
|
||||||
@ -64,6 +67,14 @@ export const useNDKStore = create<NDKStoreState & NDKStoreActions>((set, get) =>
|
|||||||
});
|
});
|
||||||
set({ relayStatus });
|
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
|
// Initialize NDK with relays
|
||||||
const ndk = new NDK({
|
const ndk = new NDK({
|
||||||
explicitRelayUrls: DEFAULT_RELAYS
|
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[][]) => {
|
publishEvent: async (kind: number, content: string, tags: string[][]) => {
|
||||||
try {
|
try {
|
||||||
const { ndk, isAuthenticated, currentUser } = get();
|
const { ndk, isAuthenticated, currentUser } = get();
|
||||||
@ -256,21 +275,24 @@ export const useNDKStore = create<NDKStoreState & NDKStoreActions>((set, get) =>
|
|||||||
throw new Error('Not authenticated');
|
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
|
// Create event
|
||||||
|
console.log('Creating event...');
|
||||||
const event = new NDKEvent(ndk);
|
const event = new NDKEvent(ndk);
|
||||||
event.kind = kind;
|
event.kind = kind;
|
||||||
event.content = content;
|
event.content = content;
|
||||||
event.tags = tags;
|
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 {
|
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
|
// Try to find and override the randomBytes function
|
||||||
const nostrTools = require('nostr-tools');
|
const nostrTools = require('nostr-tools');
|
||||||
const nobleHashes = require('@noble/hashes/utils');
|
const nobleHashes = require('@noble/hashes/utils');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user