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));
} 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');
}
}
}
+3 -8
View File
@@ -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());
}
}
}
+1 -1
View File
@@ -549,7 +549,7 @@ class _RepublishStacksButton extends HookConsumerWidget {
if (context.mounted) {
context.showError(
'Failed to republish stacks',
description: '$e',
technicalDetails: '$e',
);
}
} finally {
+31 -4
View File
@@ -11,6 +11,7 @@ extension ContextX on BuildContext {
void showInfo(
String title, {
String? description,
String? technicalDetails,
IconData? icon,
List<(String, Future<void> 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<void> 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<void> 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<void> 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<void> 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,
+1 -1
View File
@@ -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');
}
}
}
-2
View File
@@ -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');
},
),
],
+2 -1
View File
@@ -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 {
+4 -3
View File
@@ -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');
}
}
}
+3 -3
View File
@@ -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');
}
}
}
+5 -12
View File
@@ -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');
},
),
],
+3 -2
View File
@@ -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 {
+1 -1
View File
@@ -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())],
);
}
+1 -1
View File
@@ -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');
}
}
}
+3 -3
View File
@@ -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');
}
}
}());