feat: marcar legenda com markdown, barra de ações nos posts do canal e limites de mensagem na modal
This commit is contained in:
@@ -8,11 +8,11 @@ import 'package:tatchat/config/setting_keys.dart';
|
||||
import 'package:tatchat/utils/file_description.dart';
|
||||
import 'package:tatchat/utils/url_launcher.dart';
|
||||
import 'package:tatchat/widgets/mxc_image.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../../../widgets/blur_hash.dart';
|
||||
import 'html_message.dart';
|
||||
|
||||
class ImageBubble extends StatelessWidget {
|
||||
final Event event;
|
||||
@@ -121,14 +121,13 @@ class ImageBubble extends StatelessWidget {
|
||||
width: width,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppConfig.messageFontSize,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
child: HtmlMessage(
|
||||
html: event.isRichMessage
|
||||
? event.formattedText
|
||||
: fileDescription,
|
||||
room: event.room,
|
||||
textColor: textColor,
|
||||
fontSize: AppConfig.messageFontSize,
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppConfig.messageFontSize,
|
||||
|
||||
@@ -14,10 +14,12 @@ import 'package:tatchat/utils/adaptive_bottom_sheet.dart';
|
||||
import 'package:tatchat/utils/date_time_extension.dart';
|
||||
import 'package:tatchat/utils/file_description.dart';
|
||||
import 'package:tatchat/utils/matrix_sdk_extensions/matrix_locals.dart';
|
||||
import 'package:tatchat/utils/show_scaffold_dialog.dart';
|
||||
import 'package:tatchat/utils/string_color.dart';
|
||||
import 'package:tatchat/widgets/avatar.dart';
|
||||
import 'package:tatchat/widgets/matrix.dart';
|
||||
import 'package:tatchat/widgets/member_actions_popup_menu_button.dart';
|
||||
import 'package:tatchat/widgets/share_scaffold_dialog.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
@@ -247,8 +249,8 @@ class Message extends StatelessWidget {
|
||||
child: Center(child: Icon(Icons.check_outlined)),
|
||||
),
|
||||
direction: AppSettings.swipeRightToLeftToReply.value
|
||||
? SwipeDirection.endToStart
|
||||
: SwipeDirection.startToEnd,
|
||||
? SwipeDirection.startToEnd
|
||||
: SwipeDirection.endToStart,
|
||||
onSwipe: (_) => onSwipe(),
|
||||
child: Container(
|
||||
constraints: card
|
||||
@@ -683,7 +685,7 @@ class Message extends StatelessWidget {
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: !hasReactions
|
||||
child: card || !hasReactions
|
||||
? const SizedBox.shrink()
|
||||
: Container(
|
||||
alignment: card
|
||||
@@ -699,6 +701,17 @@ class Message extends StatelessWidget {
|
||||
child: MessageReactions(event, timeline),
|
||||
),
|
||||
),
|
||||
if (card)
|
||||
_PostActionBar(
|
||||
event: event,
|
||||
timeline: timeline,
|
||||
hasReactions: hasReactions,
|
||||
threadCount: threadChildren.length,
|
||||
onAddReaction: () => onSelect(event),
|
||||
onOpenThread: enterThread == null
|
||||
? null
|
||||
: () => enterThread(event.eventId),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: card
|
||||
? .start
|
||||
@@ -990,7 +1003,7 @@ class Message extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
if (enterThread != null)
|
||||
if (enterThread != null && !card)
|
||||
AnimatedSize(
|
||||
duration: FluffyThemes.animationDuration,
|
||||
curve: FluffyThemes.animationCurve,
|
||||
@@ -1177,3 +1190,115 @@ class __AnimateInState extends State<_AnimateIn> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PostActionBar extends StatelessWidget {
|
||||
final Event event;
|
||||
final Timeline timeline;
|
||||
final bool hasReactions;
|
||||
final int threadCount;
|
||||
final VoidCallback? onAddReaction;
|
||||
final VoidCallback? onOpenThread;
|
||||
|
||||
const _PostActionBar({
|
||||
required this.event,
|
||||
required this.timeline,
|
||||
required this.hasReactions,
|
||||
required this.threadCount,
|
||||
required this.onAddReaction,
|
||||
required this.onOpenThread,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(
|
||||
context,
|
||||
).colorScheme.surfaceContainerHighest.withAlpha(150),
|
||||
border: Border(
|
||||
top: BorderSide(
|
||||
color: Theme.of(context).colorScheme.outlineVariant.withAlpha(120),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
_BarAction(
|
||||
icon: Icons.visibility_outlined,
|
||||
label: '${event.receipts.length}',
|
||||
),
|
||||
Expanded(
|
||||
child: Row(
|
||||
children: [
|
||||
if (hasReactions)
|
||||
Flexible(
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: MessageReactions(event, timeline),
|
||||
),
|
||||
),
|
||||
_BarAction(
|
||||
icon: Icons.add_reaction_outlined,
|
||||
onPressed: onAddReaction,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_BarAction(
|
||||
icon: Icons.chat_bubble_outline,
|
||||
label: '$threadCount',
|
||||
onPressed: onOpenThread,
|
||||
),
|
||||
_BarAction(
|
||||
icon: Icons.share_outlined,
|
||||
onPressed: () {
|
||||
showScaffoldDialog(
|
||||
context: context,
|
||||
builder: (context) => ShareScaffoldDialog(
|
||||
items: [ContentShareItem(event.content.copy())],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _BarAction extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String? label;
|
||||
final VoidCallback? onPressed;
|
||||
|
||||
const _BarAction({required this.icon, this.label, this.onPressed});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
return InkWell(
|
||||
onTap: onPressed,
|
||||
borderRadius: BorderRadius.circular(AppConfig.borderRadius / 2),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 6),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 18, color: theme.colorScheme.onSurfaceVariant),
|
||||
if (label != null) ...[
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
label!,
|
||||
style: TextStyle(
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,29 +78,32 @@ class MessageContent extends StatelessWidget {
|
||||
: BoxFit.cover;
|
||||
if (w != null && h != null) {
|
||||
fit = BoxFit.contain;
|
||||
if (fullWidthCard &&
|
||||
event.messageType == MessageTypes.Image &&
|
||||
w > 0 &&
|
||||
h > 0) {
|
||||
return AspectRatio(
|
||||
aspectRatio: w / h,
|
||||
child: ImageBubble(
|
||||
event,
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
fit: BoxFit.cover,
|
||||
borderRadius: borderRadius,
|
||||
timeline: timeline,
|
||||
textColor: textColor,
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (_) => ImageViewer(
|
||||
event,
|
||||
timeline: timeline,
|
||||
outerContext: context,
|
||||
if (fullWidthCard && event.messageType == MessageTypes.Image) {
|
||||
return LayoutBuilder(
|
||||
builder: (context, constraints) {
|
||||
final maxWidth = constraints.maxWidth;
|
||||
final cardWidth = maxWidth.isFinite && maxWidth > 0
|
||||
? maxWidth
|
||||
: maxSize;
|
||||
final hasKnownRatio = w > 0 && h > 0;
|
||||
return ImageBubble(
|
||||
event,
|
||||
width: cardWidth,
|
||||
height: hasKnownRatio ? cardWidth * h / w : maxSize,
|
||||
fit: hasKnownRatio ? BoxFit.cover : BoxFit.contain,
|
||||
borderRadius: borderRadius,
|
||||
timeline: timeline,
|
||||
textColor: textColor,
|
||||
onTap: () => showDialog(
|
||||
context: context,
|
||||
builder: (_) => ImageViewer(
|
||||
event,
|
||||
timeline: timeline,
|
||||
outerContext: context,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
if (w > h) {
|
||||
|
||||
@@ -13,11 +13,11 @@ import 'package:tatchat/utils/platform_infos.dart';
|
||||
import 'package:tatchat/utils/url_launcher.dart';
|
||||
import 'package:tatchat/widgets/blur_hash.dart';
|
||||
import 'package:tatchat/widgets/mxc_image.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'package:material_ui/material_ui.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import '../../image_viewer/image_viewer.dart';
|
||||
import 'html_message.dart';
|
||||
|
||||
class EventVideoPlayer extends StatelessWidget {
|
||||
final Event event;
|
||||
@@ -151,14 +151,13 @@ class EventVideoPlayer extends StatelessWidget {
|
||||
width: width,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: Linkify(
|
||||
text: fileDescription,
|
||||
textScaleFactor: MediaQuery.textScalerOf(context).scale(1),
|
||||
style: TextStyle(
|
||||
color: textColor,
|
||||
fontSize: AppConfig.messageFontSize,
|
||||
),
|
||||
options: const LinkifyOptions(humanize: false),
|
||||
child: HtmlMessage(
|
||||
html: event.isRichMessage
|
||||
? event.formattedText
|
||||
: fileDescription,
|
||||
room: event.room,
|
||||
textColor: textColor!,
|
||||
fontSize: AppConfig.messageFontSize,
|
||||
linkStyle: TextStyle(
|
||||
color: linkColor,
|
||||
fontSize: AppConfig.messageFontSize,
|
||||
|
||||
Reference in New Issue
Block a user