diff --git a/lib/screens/app_detail_screen.dart b/lib/screens/app_detail_screen.dart index 9b5818f..be36b25 100644 --- a/lib/screens/app_detail_screen.dart +++ b/lib/screens/app_detail_screen.dart @@ -645,7 +645,7 @@ class _AppDetailContent extends HookConsumerWidget { SharePlus.instance.share(ShareParams(text: shareUrl)); } catch (e) { if (context.mounted) { - context.showError('Failed to share app', description: '$e'); + context.showError('Failed to share app', technicalDetails: '$e'); } } } @@ -654,10 +654,9 @@ class _AppDetailContent extends HookConsumerWidget { try { final shareUrl = _getAppUrl(app); Clipboard.setData(ClipboardData(text: shareUrl)); - context.showInfo('Link copied to clipboard'); } catch (e) { if (context.mounted) { - context.showError('Failed to copy link', description: '$e'); + context.showError('Failed to copy link', technicalDetails: '$e'); } } } @@ -709,7 +708,8 @@ class _AppDetailContent extends HookConsumerWidget { context.showError( 'Could not read existing saved apps', description: - 'Your previous saved apps could not be decrypted. Starting fresh.\n\n$e', + 'Your previous saved apps could not be decrypted. Starting fresh.', + technicalDetails: '$e', ); } } @@ -752,7 +752,7 @@ class _AppDetailContent extends HookConsumerWidget { } } catch (e) { if (context.mounted) { - context.showError('Failed to update bookmark', description: '$e'); + context.showError('Failed to update bookmark', technicalDetails: '$e'); } } } @@ -776,7 +776,7 @@ class _AppDetailContent extends HookConsumerWidget { } } catch (e) { if (context.mounted) { - context.showError('Failed to open browser', description: '$e'); + context.showError('Failed to open browser', technicalDetails: '$e'); } } } @@ -790,7 +790,8 @@ class _AppDetailContent extends HookConsumerWidget { context.showError( 'Failed to launch ${app.name ?? app.identifier}', description: - 'The app may have been uninstalled or moved. Try reinstalling.\n\n$e', + 'The app may have been uninstalled or moved. Try reinstalling.', + technicalDetails: '$e', ); } } @@ -808,7 +809,7 @@ class _AppDetailContent extends HookConsumerWidget { // Don't show error for user cancellation final message = e.toString(); if (!message.contains('cancelled')) { - context.showError('Uninstall failed', description: '$e'); + context.showError('Uninstall failed', technicalDetails: '$e'); } } } diff --git a/lib/screens/profile_screen.dart b/lib/screens/profile_screen.dart index b21d41e..3baf8a4 100644 --- a/lib/screens/profile_screen.dart +++ b/lib/screens/profile_screen.dart @@ -140,7 +140,7 @@ class _AuthenticationSection extends ConsumerWidget { pubkey: pubkey, profile: profile, avatarRadius: 32, - onCopiedNpub: () => context.showInfo('Copied npub to clipboard'), + onCopiedNpub: null, ), const SizedBox(height: 12), // Profile bio with NoteParser (non-interactive) @@ -250,7 +250,7 @@ class _AuthenticationSection extends ConsumerWidget { await ref.read(amberSignerProvider).signOut(); } catch (e) { if (context.mounted) { - context.showError('Sign out failed', description: '$e'); + context.showError('Sign out failed', technicalDetails: '$e'); } } } @@ -1083,11 +1083,6 @@ class _DebugMessagesSection extends HookConsumerWidget { constraints: const BoxConstraints(), onPressed: () { Clipboard.setData(ClipboardData(text: req)); - context.showInfo( - 'REQ filter copied', - description: - 'Paste into a Nostr client to debug this query.', - ); }, ), ], @@ -1455,7 +1450,7 @@ class _DataManagementSection extends ConsumerWidget { } catch (e) { if (context.mounted) { Navigator.of(context, rootNavigator: true).pop(); - context.showError('Restart failed', description: e.toString()); + context.showError('Restart failed', technicalDetails: e.toString()); } } } diff --git a/lib/screens/user_screen.dart b/lib/screens/user_screen.dart index a31b6e4..f8512ab 100644 --- a/lib/screens/user_screen.dart +++ b/lib/screens/user_screen.dart @@ -549,7 +549,7 @@ class _RepublishStacksButton extends HookConsumerWidget { if (context.mounted) { context.showError( 'Failed to republish stacks', - description: '$e', + technicalDetails: '$e', ); } } finally { diff --git a/lib/services/notification_service.dart b/lib/services/notification_service.dart index 53e1940..41462b4 100644 --- a/lib/services/notification_service.dart +++ b/lib/services/notification_service.dart @@ -11,6 +11,7 @@ extension ContextX on BuildContext { void showInfo( String title, { String? description, + String? technicalDetails, IconData? icon, List<(String, Future Function())> actions = const [], }) { @@ -18,6 +19,7 @@ extension ContextX on BuildContext { context: this, title: title, description: description, + technicalDetails: technicalDetails, icon: icon ?? Icons.info_outline_rounded, type: _ToastType.info, actions: actions, @@ -27,6 +29,7 @@ extension ContextX on BuildContext { void showError( String title, { String? description, + String? technicalDetails, IconData? icon, List<(String, Future Function())> actions = const [], }) { @@ -34,6 +37,7 @@ extension ContextX on BuildContext { context: this, title: title, description: description, + technicalDetails: technicalDetails, icon: icon ?? Icons.error_outline_rounded, type: _ToastType.error, actions: actions, @@ -49,6 +53,7 @@ void _showCustomToast({ required BuildContext context, required String title, String? description, + String? technicalDetails, required IconData icon, required _ToastType type, List<(String, Future Function())> actions = const [], @@ -66,6 +71,7 @@ void _showCustomToast({ builder: (context) => _ToastOverlay( title: title, description: description, + technicalDetails: technicalDetails, icon: icon, type: type, actions: actions, @@ -83,6 +89,7 @@ void _showCustomToast({ class _ToastOverlay extends HookWidget { final String title; final String? description; + final String? technicalDetails; final IconData icon; final _ToastType type; final List<(String, Future Function())> actions; @@ -91,6 +98,7 @@ class _ToastOverlay extends HookWidget { const _ToastOverlay({ required this.title, this.description, + this.technicalDetails, required this.icon, required this.type, required this.actions, @@ -233,6 +241,7 @@ class _ToastOverlay extends HookWidget { accentColor: accentColor, title: title, description: description, + technicalDetails: technicalDetails, actions: actions, onDismiss: dismiss, ), @@ -257,6 +266,7 @@ class _ToastContent extends HookWidget { final Color accentColor; final String title; final String? description; + final String? technicalDetails; final List<(String, Future Function())> actions; final VoidCallback onDismiss; @@ -266,6 +276,7 @@ class _ToastContent extends HookWidget { required this.accentColor, required this.title, this.description, + this.technicalDetails, required this.actions, required this.onDismiss, }); @@ -275,8 +286,10 @@ class _ToastContent extends HookWidget { final detailsExpanded = useState(false); final hasDescription = description != null && description!.isNotEmpty; + final hasTechnicalDetails = + technicalDetails != null && technicalDetails!.isNotEmpty; final hasActions = actions.isNotEmpty; - final hasExtraContent = hasDescription || hasActions; + final hasExtraContent = hasDescription || hasTechnicalDetails || hasActions; // Use Stack to position X at top-right always return Stack( @@ -318,8 +331,22 @@ class _ToastContent extends HookWidget { height: 1.35, ), ), - // Expandable technical details + // Inline description (always visible) if (hasDescription) ...[ + const SizedBox(height: 4), + Text( + description!, + style: TextStyle( + fontFamily: kFontFamily, + fontSize: 13, + fontWeight: FontWeight.w400, + color: Colors.white.withValues(alpha: 0.85), + height: 1.35, + ), + ), + ], + // Expandable technical details + if (hasTechnicalDetails) ...[ const SizedBox(height: 8), GestureDetector( onTap: () { @@ -348,7 +375,7 @@ class _ToastContent extends HookWidget { ], ), ), - // Expanded description + // Expanded technical details if (detailsExpanded.value) ...[ const SizedBox(height: 6), Container( @@ -358,7 +385,7 @@ class _ToastContent extends HookWidget { borderRadius: BorderRadius.circular(6), ), child: SelectableText( - description!, + technicalDetails!, style: TextStyle( fontFamily: 'monospace', fontSize: 11, diff --git a/lib/widgets/app_detail_widgets.dart b/lib/widgets/app_detail_widgets.dart index f08de69..2b2ae71 100644 --- a/lib/widgets/app_detail_widgets.dart +++ b/lib/widgets/app_detail_widgets.dart @@ -257,7 +257,7 @@ class SocialActionsRow extends HookConsumerWidget { } } catch (e) { if (context.mounted) { - context.showError('Failed to save app', description: '$e'); + context.showError('Failed to save app', technicalDetails: '$e'); } } } diff --git a/lib/widgets/app_info_table.dart b/lib/widgets/app_info_table.dart index c107b07..7522495 100644 --- a/lib/widgets/app_info_table.dart +++ b/lib/widgets/app_info_table.dart @@ -7,7 +7,6 @@ import 'package:models/models.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:zapstore/utils/extensions.dart'; import 'package:zapstore/widgets/download_text_container.dart'; -import 'package:zapstore/services/notification_service.dart'; import 'package:zapstore/theme.dart'; class AppInfoTable extends HookConsumerWidget { @@ -313,7 +312,6 @@ class _InfoRow extends StatelessWidget { ), onPressed: () { Clipboard.setData(ClipboardData(text: copyValue!)); - context.showInfo('Copied to clipboard'); }, ), ], diff --git a/lib/widgets/auth_widgets.dart b/lib/widgets/auth_widgets.dart index 98206e8..2d967f4 100644 --- a/lib/widgets/auth_widgets.dart +++ b/lib/widgets/auth_widgets.dart @@ -46,7 +46,8 @@ class SignInPrompt extends HookConsumerWidget { context.showError( 'Sign-in failed', description: - 'Amber could not complete the sign-in. Make sure Amber is installed and try again.\n\n$e', + 'Amber could not complete the sign-in. Make sure Amber is installed and try again.', + technicalDetails: '$e', ); } } finally { diff --git a/lib/widgets/bookmark_widgets.dart b/lib/widgets/bookmark_widgets.dart index cc4ad35..684decd 100644 --- a/lib/widgets/bookmark_widgets.dart +++ b/lib/widgets/bookmark_widgets.dart @@ -132,7 +132,8 @@ class SaveAppDialog extends HookConsumerWidget { context.showError( 'Could not read existing saved apps', description: - 'Your previous saved apps could not be decrypted. Starting fresh.\n\n$e', + 'Your previous saved apps could not be decrypted. Starting fresh.', + technicalDetails: '$e', ); } } @@ -176,7 +177,7 @@ class SaveAppDialog extends HookConsumerWidget { } } catch (e) { if (context.mounted) { - context.showError('Failed to update bookmark', description: '$e'); + context.showError('Failed to update bookmark', technicalDetails: '$e'); } } } @@ -695,7 +696,7 @@ class _AddToStackDialogSignedIn extends HookConsumerWidget { } } catch (e) { if (context.mounted) { - context.showError('Failed to save', description: '$e'); + context.showError('Failed to save', technicalDetails: '$e'); } } } diff --git a/lib/widgets/comments_section.dart b/lib/widgets/comments_section.dart index e527877..f683c44 100644 --- a/lib/widgets/comments_section.dart +++ b/lib/widgets/comments_section.dart @@ -683,7 +683,7 @@ class _CommentComposer extends HookConsumerWidget { } } catch (e) { if (context.mounted) { - context.showError('Failed to post comment', description: '$e'); + context.showError('Failed to post comment', technicalDetails: '$e'); } } } @@ -895,7 +895,7 @@ class _ReplyComposer extends HookConsumerWidget { } } catch (e) { if (context.mounted) { - context.showError('Failed to post reply', description: '$e'); + context.showError('Failed to post reply', technicalDetails: '$e'); } } } @@ -1025,7 +1025,7 @@ class _StackCommentComposer extends HookConsumerWidget { } } catch (e) { if (context.mounted) { - context.showError('Failed to post comment', description: '$e'); + context.showError('Failed to post comment', technicalDetails: '$e'); } } } diff --git a/lib/widgets/install_button.dart b/lib/widgets/install_button.dart index 64a7fff..a1154d6 100644 --- a/lib/widgets/install_button.dart +++ b/lib/widgets/install_button.dart @@ -609,7 +609,8 @@ class InstallButton extends ConsumerWidget { if (!context.mounted) return; context.showError( 'Failed to launch ${app.name ?? app.identifier}', - description: 'The app may have been uninstalled or moved.\n\n$e', + description: 'The app may have been uninstalled or moved.', + technicalDetails: '$e', ); } } @@ -622,7 +623,7 @@ class InstallButton extends ConsumerWidget { if (context.mounted) { final errorMessage = e.toString(); if (!errorMessage.contains('cancelled')) { - context.showError('Uninstall failed', description: errorMessage); + context.showError('Uninstall failed', technicalDetails: errorMessage); } } } @@ -657,19 +658,12 @@ class InstallButton extends ConsumerWidget { context.showError( operation.message, - description: operation.description, + technicalDetails: operation.description, actions: [ ( 'Copy', () async { await Clipboard.setData(ClipboardData(text: errorText)); - if (context.mounted) { - context.showInfo( - 'Copied to clipboard', - description: - 'Share on nostr or the zapstore Signal support group.', - ); - } }, ), ], @@ -768,7 +762,7 @@ class InstallButton extends ConsumerWidget { } catch (e) { final errorMessage = e.toString(); if (context.mounted && !errorMessage.contains('cancelled')) { - context.showError('Update failed', description: errorMessage); + context.showError('Update failed', technicalDetails: errorMessage); } } } @@ -816,7 +810,6 @@ class InstallButton extends ConsumerWidget { tooltip: 'Copy certificate hash', onPressed: () { Clipboard.setData(ClipboardData(text: hash)); - context.showInfo('Copied $tooltipText'); }, ), ], diff --git a/lib/widgets/nwc_widgets.dart b/lib/widgets/nwc_widgets.dart index ea41ccf..81a06fd 100644 --- a/lib/widgets/nwc_widgets.dart +++ b/lib/widgets/nwc_widgets.dart @@ -206,7 +206,7 @@ class NWCConnectionCard extends HookConsumerWidget { } } catch (e) { if (context.mounted) { - context.showError('Failed to disconnect wallet', description: '$e'); + context.showError('Failed to disconnect wallet', technicalDetails: '$e'); } } } @@ -337,7 +337,8 @@ class NWCConnectionDialog extends HookWidget { context.showError( 'Wallet connection failed', description: - 'Could not connect to the wallet. Verify the connection string and try again.\n\n$e', + 'Could not connect to the wallet. Verify the connection string and try again.', + technicalDetails: '$e', ); } } finally { diff --git a/lib/widgets/relay_management_card.dart b/lib/widgets/relay_management_card.dart index 8bd3fde..753f93b 100644 --- a/lib/widgets/relay_management_card.dart +++ b/lib/widgets/relay_management_card.dart @@ -177,7 +177,7 @@ class RelayManagementCard extends HookConsumerWidget { if (context.mounted) { context.showError( 'Failed to apply relay changes', - description: '$e', + technicalDetails: '$e', actions: [('Retry', () => applyChanges())], ); } diff --git a/lib/widgets/sign_in_button.dart b/lib/widgets/sign_in_button.dart index ea727b3..39c028d 100644 --- a/lib/widgets/sign_in_button.dart +++ b/lib/widgets/sign_in_button.dart @@ -43,7 +43,7 @@ class SignInButton extends ConsumerWidget { await onSignInSuccess(ref.read(refProvider)); } catch (e) { if (context.mounted) { - context.showError('Sign-in failed: $e'); + context.showError('Sign-in failed', technicalDetails: '$e'); } } } diff --git a/lib/widgets/zap_widgets.dart b/lib/widgets/zap_widgets.dart index 29ea330..2e5f3f1 100644 --- a/lib/widgets/zap_widgets.dart +++ b/lib/widgets/zap_widgets.dart @@ -500,14 +500,14 @@ class ZapAmountDialog extends HookConsumerWidget { if (toastContext != null && toastContext.mounted) { toastContext.showError( 'Zap failed', - description: '$e', + technicalDetails: '$e', ); } } }()); } catch (e) { if (context.mounted) { - context.showError('Zap failed', description: '$e'); + context.showError('Zap failed', technicalDetails: '$e'); Navigator.of(context).pop(false); } } @@ -696,7 +696,7 @@ class NWCZapDialog extends HookConsumerWidget { } } catch (e) { if (toastContext != null && toastContext.mounted) { - toastContext.showError('Zap failed', description: '$e'); + toastContext.showError('Zap failed', technicalDetails: '$e'); } } }());