diff --git a/lib/constants/app_constants.dart b/lib/constants/app_constants.dart index 56c9f7a..8ab1af9 100644 --- a/lib/constants/app_constants.dart +++ b/lib/constants/app_constants.dart @@ -12,11 +12,5 @@ const kFranzapPubkey = /// Shareable identifier for the Nostr curation set const kNostrCurationSetShareableId = '30267:$kFranzapPubkey:nostr'; -/// Anonymous private key for error reporting and anonymous operations -/// This key is used when the user is not signed in -const kAnonymousPrivateKey = - 'c86eda2daae768374526bc54903f388d9a866c00740ec8db418d7ef2dca77b5b'; - /// Identifier for storing user bookmarks const kAppBookmarksIdentifier = 'zapstore-bookmarks'; - diff --git a/lib/services/error_reporting_service.dart b/lib/services/error_reporting_service.dart index 5f11ff0..a227bb1 100644 --- a/lib/services/error_reporting_service.dart +++ b/lib/services/error_reporting_service.dart @@ -7,8 +7,8 @@ import 'package:zapstore/utils/extensions.dart'; /// Service for reporting errors via NIP-44 encrypted DMs to Zapstore team. /// -/// Uses [kAnonymousPrivateKey] for signing so error reports can be sent -/// even when the user is not signed in. +/// Generates an ephemeral signer per report so errors can be sent even when +/// the user is not signed in. class ErrorReportingService { ErrorReportingService(this.ref); @@ -31,8 +31,6 @@ class ErrorReportingService { Future reportError(Object exception, StackTrace? stackTrace) async { // Don't report in debug mode if (kDebugMode) { - debugPrint('Error (not reported in debug): $exception'); - debugPrint('$stackTrace'); return; } @@ -50,8 +48,8 @@ class ErrorReportingService { } try { - // Create anonymous signer for error reporting - final signer = Bip340PrivateKeySigner(kAnonymousPrivateKey, ref); + // Create ephemeral signer for error reporting + final signer = Bip340PrivateKeySigner(Utils.generateRandomHex64(), ref); await signer.signIn(setAsActive: false, registerSigner: false); // Format error report @@ -65,19 +63,15 @@ class ErrorReportingService { // Sign and publish final signedDm = await dm.signWith(signer); - await ref.read(storageNotifierProvider.notifier).publish( - {signedDm}, - source: const RemoteSource(relays: 'social', stream: false), - ); + await ref.read(storageNotifierProvider.notifier).publish({ + signedDm, + }, source: const RemoteSource(relays: 'social', stream: false)); // Update rate limiting _reportedErrors[errorHash] = DateTime.now(); _sessionReportCount++; - - debugPrint('Error report sent successfully'); } catch (e) { // Silently fail - we don't want error reporting to cause more errors - debugPrint('Failed to send error report: $e'); } } @@ -112,4 +106,3 @@ class ErrorReportingService { final errorReportingServiceProvider = Provider( ErrorReportingService.new, ); - diff --git a/lib/widgets/comments_section.dart b/lib/widgets/comments_section.dart index a09f615..a61ccd0 100644 --- a/lib/widgets/comments_section.dart +++ b/lib/widgets/comments_section.dart @@ -66,7 +66,8 @@ class CommentsSection extends ConsumerWidget { context, ).textTheme.titleLarge?.copyWith(fontWeight: FontWeight.bold), ), - if (comments.isNotEmpty) _CommentCountBadge(count: comments.length), + if (comments.isNotEmpty) + _CommentCountBadge(count: comments.length), ], ), const SizedBox(height: 12), @@ -80,7 +81,8 @@ class CommentsSection extends ConsumerWidget { // Comments list - only when there are comments if (comments.isNotEmpty) ...[ const SizedBox(height: 16), - ...(comments.toList()..sort((a, b) => b.createdAt.compareTo(a.createdAt))) + ...(comments.toList() + ..sort((a, b) => b.createdAt.compareTo(a.createdAt))) .map( (comment) => Padding( padding: const EdgeInsets.only(bottom: 16), @@ -154,8 +156,8 @@ class _CommentCard extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { final author = comment.author.value; - // Extract version from d tag (thread key) - final version = comment.event.getFirstTagValue('d'); + // Extract version from v tag (per NIP-22 guidance) + final version = comment.event.getFirstTagValue('v'); return Card( margin: EdgeInsets.zero, @@ -408,8 +410,8 @@ class _CommentComposer extends HookConsumerWidget { // No parentModel for root comments - only A/K/P tags, no e/k/p ); - // Add d tag as thread key (version) - comment.event.addTagValue('d', versionToComment); + // Add v tag for version (per NIP-22 guidance, not d tag) + comment.event.addTagValue('v', versionToComment); final signedComment = await comment.signWith(signer); diff --git a/lib/widgets/zap_widgets.dart b/lib/widgets/zap_widgets.dart index 4f870c1..0009016 100644 --- a/lib/widgets/zap_widgets.dart +++ b/lib/widgets/zap_widgets.dart @@ -473,11 +473,11 @@ class ZapAmountDialog extends HookConsumerWidget { try { final navigator = Navigator.of(context); - // Prepare signer (anonymous if needed) + // Prepare signer (ephemeral if needed) var signer = ref.read(Signer.activeSignerProvider); if (signer == null) { signer = Bip340PrivateKeySigner( - kAnonymousPrivateKey, + Utils.generateRandomHex64(), ref.ref, ); await signer.signIn(registerSigner: false); @@ -489,6 +489,7 @@ class ZapAmountDialog extends HookConsumerWidget { // Build zap request final latestMetadata = app.latestFileMetadata; final author = app.author.value; + if (latestMetadata == null || author == null) { throw Exception( 'App or author not ready. Please try again.', @@ -521,17 +522,24 @@ class ZapAmountDialog extends HookConsumerWidget { // Fire payment in background - errors shown via ScaffoldMessenger // ignore: unawaited_futures - _executeZapPayment(signedZapRequest, nwcString, ref.ref) - .catchError((Object e, StackTrace st) { - debugPrint('Zap payment failed: $e\n$st'); - messenger?.showSnackBar( + _executeZapPayment( + signedZapRequest, + nwcString, + ref.ref, + ).then( + (_) {}, + onError: (Object e, StackTrace st) { + if (messenger != null) { + messenger.showSnackBar( SnackBar( content: Text('Zap failed: $e'), backgroundColor: Colors.red, + duration: const Duration(seconds: 5), ), ); - return Future.error(e, st); - }); + } + }, + ); } else { final invoice = await signedZapRequest.getInvoice(); await Clipboard.setData(ClipboardData(text: invoice)); @@ -586,11 +594,14 @@ Future _executeZapPayment( ) async { final lightningInvoice = await signedZapRequest.getInvoice(); final command = PayInvoiceCommand(invoice: lightningInvoice); - return await command.execute( + + final result = await command.execute( connectionUri: nwcString, ref: ref, timeout: const Duration(seconds: 30), ); + + return result; } /// Dialog for entering a custom zap amount