mirror of
https://github.com/zjs81/meshcore-open.git
synced 2026-06-18 08:26:27 +10:00
Merge pull request #479 from MrSurly/478-implement-right-click
Issue 478: Implement right click for desktop use
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
@@ -20,6 +21,7 @@ class LinkHandler {
|
||||
required String text,
|
||||
required TextStyle style,
|
||||
TextStyle? linkStyle,
|
||||
VoidCallback? onSecondaryTap,
|
||||
}) {
|
||||
final effectiveLinkStyle = linkStyle ?? defaultLinkStyle(context, style);
|
||||
const options = LinkifyOptions(humanize: false, defaultToHttps: false);
|
||||
@@ -27,7 +29,7 @@ class LinkHandler {
|
||||
void onOpen(LinkableElement link) => handleLinkTap(context, link.url);
|
||||
|
||||
if (PlatformInfo.isDesktop) {
|
||||
return SelectableLinkify(
|
||||
final linkify = SelectableLinkify(
|
||||
text: text,
|
||||
style: style,
|
||||
linkStyle: effectiveLinkStyle,
|
||||
@@ -35,6 +37,14 @@ class LinkHandler {
|
||||
linkifiers: linkifiers,
|
||||
onOpen: onOpen,
|
||||
);
|
||||
if (onSecondaryTap == null) return linkify;
|
||||
return Listener(
|
||||
onPointerDown: (event) {
|
||||
if (event.buttons & kSecondaryMouseButton != 0) onSecondaryTap();
|
||||
},
|
||||
behavior: HitTestBehavior.translucent,
|
||||
child: linkify,
|
||||
);
|
||||
}
|
||||
return Linkify(
|
||||
text: text,
|
||||
|
||||
@@ -110,20 +110,32 @@ class _BleDebugLogScreenState extends State<BleDebugLogScreen> {
|
||||
final entry = entries[index];
|
||||
final time =
|
||||
'${entry.timestamp.hour.toString().padLeft(2, '0')}:${entry.timestamp.minute.toString().padLeft(2, '0')}:${entry.timestamp.second.toString().padLeft(2, '0')}';
|
||||
return GestureDetector(
|
||||
onLongPress: () async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: entry.payload
|
||||
.map(
|
||||
(b) => b
|
||||
.toRadixString(16)
|
||||
.padLeft(2, '0'),
|
||||
)
|
||||
.join(''),
|
||||
Future<void> copyHex() async {
|
||||
await Clipboard.setData(
|
||||
ClipboardData(
|
||||
text: entry.payload
|
||||
.map(
|
||||
(b) => b
|
||||
.toRadixString(16)
|
||||
.padLeft(2, '0'),
|
||||
)
|
||||
.join(''),
|
||||
),
|
||||
);
|
||||
if (context.mounted) {
|
||||
showDismissibleSnackBar(
|
||||
context,
|
||||
content: Text(
|
||||
context.l10n.debugLog_bleCopied,
|
||||
),
|
||||
);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: copyHex,
|
||||
onLongPress: copyHex,
|
||||
onSecondaryTap: copyHex,
|
||||
child: Container(
|
||||
color: MeshPalette.bg,
|
||||
padding: const EdgeInsets.symmetric(
|
||||
|
||||
@@ -646,6 +646,9 @@ class _ChannelChatScreenState extends State<ChannelChatScreen> {
|
||||
fontStyle: FontStyle.italic,
|
||||
color: textColor.withValues(alpha: 0.72),
|
||||
),
|
||||
onSecondaryTap: PlatformInfo.isDesktop
|
||||
? () => _showMessageActions(message)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
+118
-120
@@ -429,142 +429,140 @@ class _ChannelsScreenState extends State<ChannelsScreen>
|
||||
channelMessageStore,
|
||||
channel,
|
||||
),
|
||||
child: GestureDetector(
|
||||
onSecondaryTapUp: PlatformInfo.isDesktop
|
||||
? (_) => _showChannelActions(
|
||||
this.context,
|
||||
connector,
|
||||
channelMessageStore,
|
||||
channel,
|
||||
)
|
||||
: null,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Leading avatar with optional community badge
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
AvatarCircle(
|
||||
name: channelLabel,
|
||||
size: 42,
|
||||
color: iconColor,
|
||||
icon: icon,
|
||||
),
|
||||
if (isCommunityChannel)
|
||||
Positioned(
|
||||
right: -2,
|
||||
bottom: -2,
|
||||
child: Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
decoration: BoxDecoration(
|
||||
color: MeshPalette.magenta,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainerLow,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.people,
|
||||
size: 8,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
// Title + subtitle + ch chip
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
channelLabel,
|
||||
style: Theme.of(context).textTheme.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w500),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
StatusChip(
|
||||
label: 'CH ${channel.index}',
|
||||
color: MeshPalette.blue,
|
||||
fontSize: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (lastPreview.isNotEmpty) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
lastPreview,
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 11.5,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
],
|
||||
onSecondaryTap: PlatformInfo.isDesktop
|
||||
? () => _showChannelActions(
|
||||
this.context,
|
||||
connector,
|
||||
channelMessageStore,
|
||||
channel,
|
||||
)
|
||||
: null,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
// Leading avatar with optional community badge
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
AvatarCircle(
|
||||
name: channelLabel,
|
||||
size: 42,
|
||||
color: iconColor,
|
||||
icon: icon,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// Right side: time + unread badge + muted + drag handle
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
if (isCommunityChannel)
|
||||
Positioned(
|
||||
right: -2,
|
||||
bottom: -2,
|
||||
child: Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
decoration: BoxDecoration(
|
||||
color: MeshPalette.magenta,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainerLow,
|
||||
width: 2,
|
||||
),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.people,
|
||||
size: 8,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
// Title + subtitle + ch chip
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (lastTime != null)
|
||||
Text(
|
||||
_relativeTime(lastTime),
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 11,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
if (isMuted) ...[
|
||||
Icon(
|
||||
Icons.notifications_off,
|
||||
size: 14,
|
||||
color: scheme.onSurfaceVariant,
|
||||
Expanded(
|
||||
child: Text(
|
||||
channelLabel,
|
||||
style: Theme.of(context).textTheme.bodyMedium
|
||||
?.copyWith(fontWeight: FontWeight.w500),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
if (unreadCount > 0) UnreadBadge(count: unreadCount),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
StatusChip(
|
||||
label: 'CH ${channel.index}',
|
||||
color: MeshPalette.blue,
|
||||
fontSize: 10,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (lastPreview.isNotEmpty) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
lastPreview,
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 11.5,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
if (showDragHandle && dragIndex != null) ...[
|
||||
const SizedBox(width: 4),
|
||||
ReorderableDragStartListener(
|
||||
index: dragIndex,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Icon(
|
||||
Icons.drag_handle,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// Right side: time + unread badge + muted + drag handle
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (lastTime != null)
|
||||
Text(
|
||||
_relativeTime(lastTime),
|
||||
style: MeshTheme.mono(
|
||||
fontSize: 11,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (isMuted) ...[
|
||||
Icon(
|
||||
Icons.notifications_off,
|
||||
size: 14,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
],
|
||||
if (unreadCount > 0) UnreadBadge(count: unreadCount),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
if (showDragHandle && dragIndex != null) ...[
|
||||
const SizedBox(width: 4),
|
||||
ReorderableDragStartListener(
|
||||
index: dragIndex,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
child: Icon(
|
||||
Icons.drag_handle,
|
||||
color: scheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1435,6 +1435,9 @@ class _MessageBubble extends StatelessWidget {
|
||||
color: textColor.withValues(alpha: 0.72),
|
||||
fontSize: bodyFontSize * textScale,
|
||||
),
|
||||
onSecondaryTap: PlatformInfo.isDesktop
|
||||
? onLongPress
|
||||
: null,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -147,13 +147,6 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
|
||||
connector,
|
||||
index,
|
||||
);
|
||||
if (PlatformInfo.isDesktop) {
|
||||
return GestureDetector(
|
||||
onSecondaryTapUp: (_) =>
|
||||
_showContactContextMenu(contact, connector),
|
||||
child: tile,
|
||||
);
|
||||
}
|
||||
return tile;
|
||||
},
|
||||
),
|
||||
@@ -204,6 +197,9 @@ class _DiscoveryScreenState extends State<DiscoveryScreen> {
|
||||
}
|
||||
},
|
||||
onLongPress: () => _showContactContextMenu(contact, connector),
|
||||
onSecondaryTap: PlatformInfo.isDesktop
|
||||
? () => _showContactContextMenu(contact, connector)
|
||||
: null,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10),
|
||||
child: Row(
|
||||
children: [
|
||||
|
||||
@@ -430,6 +430,7 @@ class _LineOfSightMapScreenState extends State<LineOfSightMapScreen> {
|
||||
minZoom: _mapMinZoom,
|
||||
maxZoom: _mapMaxZoom,
|
||||
onLongPress: (_, point) => _addCustomPoint(point),
|
||||
onSecondaryTap: (_, point) => _addCustomPoint(point),
|
||||
onPositionChanged: (camera, hasGesture) {
|
||||
final shouldShow = camera.zoom >= _labelZoomThreshold;
|
||||
if (!_didReceivePositionUpdate ||
|
||||
|
||||
+41
-23
@@ -285,6 +285,31 @@ class _MapScreenState extends State<MapScreen> {
|
||||
);
|
||||
}
|
||||
|
||||
void _handleMapContextPress(
|
||||
BuildContext context,
|
||||
MeshCoreConnector connector,
|
||||
LatLng latLng,
|
||||
) {
|
||||
if (_isSelectingPoi) {
|
||||
setState(() {
|
||||
_isSelectingPoi = false;
|
||||
});
|
||||
_shareMarker(
|
||||
context: context,
|
||||
connector: connector,
|
||||
position: latLng,
|
||||
defaultLabel: context.l10n.map_pointOfInterest,
|
||||
flags: 'poi',
|
||||
);
|
||||
return;
|
||||
}
|
||||
_showShareMarkerAtPositionSheet(
|
||||
context: context,
|
||||
connector: connector,
|
||||
position: latLng,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Builder(
|
||||
@@ -708,24 +733,10 @@ class _MapScreenState extends State<MapScreen> {
|
||||
}
|
||||
},
|
||||
onLongPress: (_, latLng) {
|
||||
if (_isSelectingPoi) {
|
||||
setState(() {
|
||||
_isSelectingPoi = false;
|
||||
});
|
||||
_shareMarker(
|
||||
context: context,
|
||||
connector: connector,
|
||||
position: latLng,
|
||||
defaultLabel: context.l10n.map_pointOfInterest,
|
||||
flags: 'poi',
|
||||
);
|
||||
return;
|
||||
}
|
||||
_showShareMarkerAtPositionSheet(
|
||||
context: context,
|
||||
connector: connector,
|
||||
position: latLng,
|
||||
);
|
||||
_handleMapContextPress(context, connector, latLng);
|
||||
},
|
||||
onSecondaryTap: (_, latLng) {
|
||||
_handleMapContextPress(context, connector, latLng);
|
||||
},
|
||||
onPositionChanged: (camera, hasGesture) {
|
||||
// Track zoom in half-step buckets so cluster/marker
|
||||
@@ -1181,9 +1192,12 @@ class _MapScreenState extends State<MapScreen> {
|
||||
width: 48,
|
||||
height: 48,
|
||||
child: GestureDetector(
|
||||
onLongPress: () => _isBuildingPathTrace
|
||||
? _showNodeInfo(context, guess.contact)
|
||||
: null,
|
||||
onLongPress: () {
|
||||
if (_isBuildingPathTrace) _showNodeInfo(context, guess.contact);
|
||||
},
|
||||
onSecondaryTap: () {
|
||||
if (_isBuildingPathTrace) _showNodeInfo(context, guess.contact);
|
||||
},
|
||||
onTap: () => _isBuildingPathTrace
|
||||
? _addToPath(context, guess.contact, position: guess.position)
|
||||
: _selectNode(guess.contact, guessedPosition: guess.position),
|
||||
@@ -1383,8 +1397,12 @@ class _MapScreenState extends State<MapScreen> {
|
||||
height: size,
|
||||
child: GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onLongPress: () =>
|
||||
_isBuildingPathTrace ? _showNodeInfo(context, contact) : null,
|
||||
onLongPress: () {
|
||||
if (_isBuildingPathTrace) _showNodeInfo(context, contact);
|
||||
},
|
||||
onSecondaryTap: () {
|
||||
if (_isBuildingPathTrace) _showNodeInfo(context, contact);
|
||||
},
|
||||
onTap: () => _isBuildingPathTrace
|
||||
? _addToPath(context, contact)
|
||||
: _selectNode(contact),
|
||||
|
||||
@@ -50,6 +50,7 @@ class MeshCard extends StatelessWidget {
|
||||
final Widget child;
|
||||
final VoidCallback? onTap;
|
||||
final VoidCallback? onLongPress;
|
||||
final VoidCallback? onSecondaryTap;
|
||||
final EdgeInsetsGeometry padding;
|
||||
final EdgeInsetsGeometry margin;
|
||||
final Color? color;
|
||||
@@ -61,6 +62,7 @@ class MeshCard extends StatelessWidget {
|
||||
required this.child,
|
||||
this.onTap,
|
||||
this.onLongPress,
|
||||
this.onSecondaryTap,
|
||||
this.padding = const EdgeInsets.all(14),
|
||||
this.margin = const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
|
||||
this.color,
|
||||
@@ -89,6 +91,7 @@ class MeshCard extends StatelessWidget {
|
||||
HapticFeedback.selectionClick();
|
||||
onLongPress!();
|
||||
},
|
||||
onSecondaryTap: onSecondaryTap,
|
||||
child: Padding(padding: padding, child: child),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
import '../connector/meshcore_connector.dart';
|
||||
import '../utils/platform_info.dart';
|
||||
import '../helpers/path_helper.dart';
|
||||
import '../l10n/l10n.dart';
|
||||
import '../models/contact.dart';
|
||||
@@ -534,56 +535,67 @@ class _RoutingSheetBodyState extends State<_RoutingSheetBody> {
|
||||
l10n.routing_deliveryCounts(record.successCount, record.failureCount),
|
||||
];
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: ListTile(
|
||||
enabled: hasBytes,
|
||||
leading: CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: bg,
|
||||
child: Icon(
|
||||
_qualityIcon(quality),
|
||||
size: 18,
|
||||
color: fg,
|
||||
semanticLabel: _qualityLabel(context, quality),
|
||||
return GestureDetector(
|
||||
behavior: HitTestBehavior.opaque,
|
||||
onSecondaryTapUp: PlatformInfo.isDesktop && hasBytes
|
||||
? (_) =>
|
||||
_showPathDetail(context, connector, contact, record.pathBytes)
|
||||
: null,
|
||||
child: Card(
|
||||
margin: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: ListTile(
|
||||
enabled: hasBytes,
|
||||
leading: CircleAvatar(
|
||||
radius: 18,
|
||||
backgroundColor: bg,
|
||||
child: Icon(
|
||||
_qualityIcon(quality),
|
||||
size: 18,
|
||||
color: fg,
|
||||
semanticLabel: _qualityLabel(context, quality),
|
||||
),
|
||||
),
|
||||
),
|
||||
title: Text(title, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
subtitle: Text(
|
||||
'$line1\n${line2Parts.join(' • ')}',
|
||||
style: const TextStyle(fontSize: 11),
|
||||
),
|
||||
isThreeLine: true,
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (inUse)
|
||||
Tooltip(
|
||||
message: l10n.routing_inUse,
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
color: scheme.primary,
|
||||
semanticLabel: l10n.routing_inUse,
|
||||
title: Text(title, maxLines: 1, overflow: TextOverflow.ellipsis),
|
||||
subtitle: Text(
|
||||
'$line1\n${line2Parts.join(' • ')}',
|
||||
style: const TextStyle(fontSize: 11),
|
||||
),
|
||||
isThreeLine: true,
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
if (inUse)
|
||||
Tooltip(
|
||||
message: l10n.routing_inUse,
|
||||
child: Icon(
|
||||
Icons.check_circle,
|
||||
color: scheme.primary,
|
||||
semanticLabel: l10n.routing_inUse,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete_outline, size: 20),
|
||||
tooltip: l10n.chat_removePath,
|
||||
constraints: const BoxConstraints(minWidth: 44, minHeight: 44),
|
||||
onPressed: () => pathService.removePathRecord(
|
||||
contact.publicKeyHex,
|
||||
record.pathBytes,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete_outline, size: 20),
|
||||
tooltip: l10n.chat_removePath,
|
||||
constraints: const BoxConstraints(minWidth: 44, minHeight: 44),
|
||||
onPressed: () => pathService.removePathRecord(
|
||||
contact.publicKeyHex,
|
||||
record.pathBytes,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
onTap: hasBytes && !inUse
|
||||
? () => _applyHistoryPath(connector, contact, record)
|
||||
: null,
|
||||
onLongPress: hasBytes
|
||||
? () => _showPathDetail(
|
||||
context,
|
||||
connector,
|
||||
contact,
|
||||
record.pathBytes,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
onTap: hasBytes && !inUse
|
||||
? () => _applyHistoryPath(connector, contact, record)
|
||||
: null,
|
||||
onLongPress: hasBytes
|
||||
? () =>
|
||||
_showPathDetail(context, connector, contact, record.pathBytes)
|
||||
: null,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ class TranslatedMessageContent extends StatelessWidget {
|
||||
final TextStyle style;
|
||||
final TextStyle? originalStyle;
|
||||
final bool showOriginalFirst;
|
||||
final VoidCallback? onSecondaryTap;
|
||||
|
||||
const TranslatedMessageContent({
|
||||
super.key,
|
||||
@@ -16,6 +17,7 @@ class TranslatedMessageContent extends StatelessWidget {
|
||||
this.originalText,
|
||||
this.originalStyle,
|
||||
this.showOriginalFirst = true,
|
||||
this.onSecondaryTap,
|
||||
});
|
||||
|
||||
@override
|
||||
@@ -36,12 +38,14 @@ class TranslatedMessageContent extends StatelessWidget {
|
||||
fontStyle: FontStyle.italic,
|
||||
fontSize: style.fontSize,
|
||||
),
|
||||
onSecondaryTap: onSecondaryTap,
|
||||
)
|
||||
: null;
|
||||
final translatedWidget = LinkHandler.buildLinkifyText(
|
||||
context: context,
|
||||
text: trimmedDisplay,
|
||||
style: style,
|
||||
onSecondaryTap: onSecondaryTap,
|
||||
);
|
||||
|
||||
if (!shouldShowOriginal) {
|
||||
|
||||
Reference in New Issue
Block a user