Toasts: add technical details besides description, and remove confirmations of copying to clipboard

This commit is contained in:
franzap
2026-02-06 16:59:47 -03:00
parent 566fb00f20
commit 3e31195f6f
14 changed files with 67 additions and 50 deletions
+9 -8
View File
@@ -645,7 +645,7 @@ class _AppDetailContent extends HookConsumerWidget {
SharePlus.instance.share(ShareParams(text: shareUrl)); SharePlus.instance.share(ShareParams(text: shareUrl));
} catch (e) { } catch (e) {
if (context.mounted) { 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 { try {
final shareUrl = _getAppUrl(app); final shareUrl = _getAppUrl(app);
Clipboard.setData(ClipboardData(text: shareUrl)); Clipboard.setData(ClipboardData(text: shareUrl));
context.showInfo('Link copied to clipboard');
} catch (e) { } catch (e) {
if (context.mounted) { 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( context.showError(
'Could not read existing saved apps', 'Could not read existing saved apps',
description: 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) { } catch (e) {
if (context.mounted) { 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) { } catch (e) {
if (context.mounted) { 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( context.showError(
'Failed to launch ${app.name ?? app.identifier}', 'Failed to launch ${app.name ?? app.identifier}',
description: 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 // Don't show error for user cancellation
final message = e.toString(); final message = e.toString();
if (!message.contains('cancelled')) { if (!message.contains('cancelled')) {
context.showError('Uninstall failed', description: '$e'); context.showError('Uninstall failed', technicalDetails: '$e');
} }
} }
} }
+3 -8
View File
@@ -140,7 +140,7 @@ class _AuthenticationSection extends ConsumerWidget {
pubkey: pubkey, pubkey: pubkey,
profile: profile, profile: profile,
avatarRadius: 32, avatarRadius: 32,
onCopiedNpub: () => context.showInfo('Copied npub to clipboard'), onCopiedNpub: null,
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
// Profile bio with NoteParser (non-interactive) // Profile bio with NoteParser (non-interactive)
@@ -250,7 +250,7 @@ class _AuthenticationSection extends ConsumerWidget {
await ref.read(amberSignerProvider).signOut(); await ref.read(amberSignerProvider).signOut();
} catch (e) { } catch (e) {
if (context.mounted) { 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(), constraints: const BoxConstraints(),
onPressed: () { onPressed: () {
Clipboard.setData(ClipboardData(text: req)); 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) { } catch (e) {
if (context.mounted) { if (context.mounted) {
Navigator.of(context, rootNavigator: true).pop(); Navigator.of(context, rootNavigator: true).pop();
context.showError('Restart failed', description: e.toString()); context.showError('Restart failed', technicalDetails: e.toString());
} }
} }
} }
+1 -1
View File
@@ -549,7 +549,7 @@ class _RepublishStacksButton extends HookConsumerWidget {
if (context.mounted) { if (context.mounted) {
context.showError( context.showError(
'Failed to republish stacks', 'Failed to republish stacks',
description: '$e', technicalDetails: '$e',
); );
} }
} finally { } finally {
+31 -4
View File
@@ -11,6 +11,7 @@ extension ContextX on BuildContext {
void showInfo( void showInfo(
String title, { String title, {
String? description, String? description,
String? technicalDetails,
IconData? icon, IconData? icon,
List<(String, Future<void> Function())> actions = const [], List<(String, Future<void> Function())> actions = const [],
}) { }) {
@@ -18,6 +19,7 @@ extension ContextX on BuildContext {
context: this, context: this,
title: title, title: title,
description: description, description: description,
technicalDetails: technicalDetails,
icon: icon ?? Icons.info_outline_rounded, icon: icon ?? Icons.info_outline_rounded,
type: _ToastType.info, type: _ToastType.info,
actions: actions, actions: actions,
@@ -27,6 +29,7 @@ extension ContextX on BuildContext {
void showError( void showError(
String title, { String title, {
String? description, String? description,
String? technicalDetails,
IconData? icon, IconData? icon,
List<(String, Future<void> Function())> actions = const [], List<(String, Future<void> Function())> actions = const [],
}) { }) {
@@ -34,6 +37,7 @@ extension ContextX on BuildContext {
context: this, context: this,
title: title, title: title,
description: description, description: description,
technicalDetails: technicalDetails,
icon: icon ?? Icons.error_outline_rounded, icon: icon ?? Icons.error_outline_rounded,
type: _ToastType.error, type: _ToastType.error,
actions: actions, actions: actions,
@@ -49,6 +53,7 @@ void _showCustomToast({
required BuildContext context, required BuildContext context,
required String title, required String title,
String? description, String? description,
String? technicalDetails,
required IconData icon, required IconData icon,
required _ToastType type, required _ToastType type,
List<(String, Future<void> Function())> actions = const [], List<(String, Future<void> Function())> actions = const [],
@@ -66,6 +71,7 @@ void _showCustomToast({
builder: (context) => _ToastOverlay( builder: (context) => _ToastOverlay(
title: title, title: title,
description: description, description: description,
technicalDetails: technicalDetails,
icon: icon, icon: icon,
type: type, type: type,
actions: actions, actions: actions,
@@ -83,6 +89,7 @@ void _showCustomToast({
class _ToastOverlay extends HookWidget { class _ToastOverlay extends HookWidget {
final String title; final String title;
final String? description; final String? description;
final String? technicalDetails;
final IconData icon; final IconData icon;
final _ToastType type; final _ToastType type;
final List<(String, Future<void> Function())> actions; final List<(String, Future<void> Function())> actions;
@@ -91,6 +98,7 @@ class _ToastOverlay extends HookWidget {
const _ToastOverlay({ const _ToastOverlay({
required this.title, required this.title,
this.description, this.description,
this.technicalDetails,
required this.icon, required this.icon,
required this.type, required this.type,
required this.actions, required this.actions,
@@ -233,6 +241,7 @@ class _ToastOverlay extends HookWidget {
accentColor: accentColor, accentColor: accentColor,
title: title, title: title,
description: description, description: description,
technicalDetails: technicalDetails,
actions: actions, actions: actions,
onDismiss: dismiss, onDismiss: dismiss,
), ),
@@ -257,6 +266,7 @@ class _ToastContent extends HookWidget {
final Color accentColor; final Color accentColor;
final String title; final String title;
final String? description; final String? description;
final String? technicalDetails;
final List<(String, Future<void> Function())> actions; final List<(String, Future<void> Function())> actions;
final VoidCallback onDismiss; final VoidCallback onDismiss;
@@ -266,6 +276,7 @@ class _ToastContent extends HookWidget {
required this.accentColor, required this.accentColor,
required this.title, required this.title,
this.description, this.description,
this.technicalDetails,
required this.actions, required this.actions,
required this.onDismiss, required this.onDismiss,
}); });
@@ -275,8 +286,10 @@ class _ToastContent extends HookWidget {
final detailsExpanded = useState(false); final detailsExpanded = useState(false);
final hasDescription = description != null && description!.isNotEmpty; final hasDescription = description != null && description!.isNotEmpty;
final hasTechnicalDetails =
technicalDetails != null && technicalDetails!.isNotEmpty;
final hasActions = actions.isNotEmpty; final hasActions = actions.isNotEmpty;
final hasExtraContent = hasDescription || hasActions; final hasExtraContent = hasDescription || hasTechnicalDetails || hasActions;
// Use Stack to position X at top-right always // Use Stack to position X at top-right always
return Stack( return Stack(
@@ -318,8 +331,22 @@ class _ToastContent extends HookWidget {
height: 1.35, height: 1.35,
), ),
), ),
// Expandable technical details // Inline description (always visible)
if (hasDescription) ...[ 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), const SizedBox(height: 8),
GestureDetector( GestureDetector(
onTap: () { onTap: () {
@@ -348,7 +375,7 @@ class _ToastContent extends HookWidget {
], ],
), ),
), ),
// Expanded description // Expanded technical details
if (detailsExpanded.value) ...[ if (detailsExpanded.value) ...[
const SizedBox(height: 6), const SizedBox(height: 6),
Container( Container(
@@ -358,7 +385,7 @@ class _ToastContent extends HookWidget {
borderRadius: BorderRadius.circular(6), borderRadius: BorderRadius.circular(6),
), ),
child: SelectableText( child: SelectableText(
description!, technicalDetails!,
style: TextStyle( style: TextStyle(
fontFamily: 'monospace', fontFamily: 'monospace',
fontSize: 11, fontSize: 11,
+1 -1
View File
@@ -257,7 +257,7 @@ class SocialActionsRow extends HookConsumerWidget {
} }
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {
context.showError('Failed to save app', description: '$e'); context.showError('Failed to save app', technicalDetails: '$e');
} }
} }
} }
-2
View File
@@ -7,7 +7,6 @@ import 'package:models/models.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
import 'package:zapstore/utils/extensions.dart'; import 'package:zapstore/utils/extensions.dart';
import 'package:zapstore/widgets/download_text_container.dart'; import 'package:zapstore/widgets/download_text_container.dart';
import 'package:zapstore/services/notification_service.dart';
import 'package:zapstore/theme.dart'; import 'package:zapstore/theme.dart';
class AppInfoTable extends HookConsumerWidget { class AppInfoTable extends HookConsumerWidget {
@@ -313,7 +312,6 @@ class _InfoRow extends StatelessWidget {
), ),
onPressed: () { onPressed: () {
Clipboard.setData(ClipboardData(text: copyValue!)); Clipboard.setData(ClipboardData(text: copyValue!));
context.showInfo('Copied to clipboard');
}, },
), ),
], ],
+2 -1
View File
@@ -46,7 +46,8 @@ class SignInPrompt extends HookConsumerWidget {
context.showError( context.showError(
'Sign-in failed', 'Sign-in failed',
description: 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 { } finally {
+4 -3
View File
@@ -132,7 +132,8 @@ class SaveAppDialog extends HookConsumerWidget {
context.showError( context.showError(
'Could not read existing saved apps', 'Could not read existing saved apps',
description: 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) { } catch (e) {
if (context.mounted) { 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) { } catch (e) {
if (context.mounted) { if (context.mounted) {
context.showError('Failed to save', description: '$e'); context.showError('Failed to save', technicalDetails: '$e');
} }
} }
} }
+3 -3
View File
@@ -683,7 +683,7 @@ class _CommentComposer extends HookConsumerWidget {
} }
} catch (e) { } catch (e) {
if (context.mounted) { 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) { } catch (e) {
if (context.mounted) { 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) { } catch (e) {
if (context.mounted) { if (context.mounted) {
context.showError('Failed to post comment', description: '$e'); context.showError('Failed to post comment', technicalDetails: '$e');
} }
} }
} }
+5 -12
View File
@@ -609,7 +609,8 @@ class InstallButton extends ConsumerWidget {
if (!context.mounted) return; if (!context.mounted) return;
context.showError( context.showError(
'Failed to launch ${app.name ?? app.identifier}', '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) { if (context.mounted) {
final errorMessage = e.toString(); final errorMessage = e.toString();
if (!errorMessage.contains('cancelled')) { 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( context.showError(
operation.message, operation.message,
description: operation.description, technicalDetails: operation.description,
actions: [ actions: [
( (
'Copy', 'Copy',
() async { () async {
await Clipboard.setData(ClipboardData(text: errorText)); 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) { } catch (e) {
final errorMessage = e.toString(); final errorMessage = e.toString();
if (context.mounted && !errorMessage.contains('cancelled')) { 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', tooltip: 'Copy certificate hash',
onPressed: () { onPressed: () {
Clipboard.setData(ClipboardData(text: hash)); Clipboard.setData(ClipboardData(text: hash));
context.showInfo('Copied $tooltipText');
}, },
), ),
], ],
+3 -2
View File
@@ -206,7 +206,7 @@ class NWCConnectionCard extends HookConsumerWidget {
} }
} catch (e) { } catch (e) {
if (context.mounted) { 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( context.showError(
'Wallet connection failed', 'Wallet connection failed',
description: 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 { } finally {
+1 -1
View File
@@ -177,7 +177,7 @@ class RelayManagementCard extends HookConsumerWidget {
if (context.mounted) { if (context.mounted) {
context.showError( context.showError(
'Failed to apply relay changes', 'Failed to apply relay changes',
description: '$e', technicalDetails: '$e',
actions: [('Retry', () => applyChanges())], actions: [('Retry', () => applyChanges())],
); );
} }
+1 -1
View File
@@ -43,7 +43,7 @@ class SignInButton extends ConsumerWidget {
await onSignInSuccess(ref.read(refProvider)); await onSignInSuccess(ref.read(refProvider));
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {
context.showError('Sign-in failed: $e'); context.showError('Sign-in failed', technicalDetails: '$e');
} }
} }
} }
+3 -3
View File
@@ -500,14 +500,14 @@ class ZapAmountDialog extends HookConsumerWidget {
if (toastContext != null && toastContext.mounted) { if (toastContext != null && toastContext.mounted) {
toastContext.showError( toastContext.showError(
'Zap failed', 'Zap failed',
description: '$e', technicalDetails: '$e',
); );
} }
} }
}()); }());
} catch (e) { } catch (e) {
if (context.mounted) { if (context.mounted) {
context.showError('Zap failed', description: '$e'); context.showError('Zap failed', technicalDetails: '$e');
Navigator.of(context).pop(false); Navigator.of(context).pop(false);
} }
} }
@@ -696,7 +696,7 @@ class NWCZapDialog extends HookConsumerWidget {
} }
} catch (e) { } catch (e) {
if (toastContext != null && toastContext.mounted) { if (toastContext != null && toastContext.mounted) {
toastContext.showError('Zap failed', description: '$e'); toastContext.showError('Zap failed', technicalDetails: '$e');
} }
} }
}()); }());