largura imagem mural, comentarios
Some checks failed
Main Deploy Workflow / deploy_web (push) Has been cancelled
Main Deploy Workflow / deploy_playstore_internal (push) Has been cancelled

This commit is contained in:
2026-09-14 13:47:16 -03:00
parent ebafa1dc39
commit b17c22321e
4 changed files with 604 additions and 184 deletions

View File

@@ -16,6 +16,7 @@ import 'package:tatchat/pages/chat/sticker_picker_dialog.dart';
import 'package:tatchat/pages/image_viewer/video_player.dart'; import 'package:tatchat/pages/image_viewer/video_player.dart';
import 'package:tatchat/utils/adaptive_bottom_sheet.dart'; import 'package:tatchat/utils/adaptive_bottom_sheet.dart';
import 'package:tatchat/utils/matrix_sdk_extensions/matrix_locals.dart'; import 'package:tatchat/utils/matrix_sdk_extensions/matrix_locals.dart';
import 'package:tatchat/utils/show_scaffold_dialog.dart';
import 'package:tatchat/utils/stream_extension.dart'; import 'package:tatchat/utils/stream_extension.dart';
import 'package:tatchat/utils/url_launcher.dart'; import 'package:tatchat/utils/url_launcher.dart';
import 'package:tatchat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart'; import 'package:tatchat/widgets/adaptive_dialogs/show_ok_cancel_alert_dialog.dart';
@@ -24,6 +25,7 @@ import 'package:tatchat/widgets/adaptive_dialogs/user_dialog.dart';
import 'package:tatchat/widgets/avatar.dart'; import 'package:tatchat/widgets/avatar.dart';
import 'package:tatchat/widgets/matrix.dart'; import 'package:tatchat/widgets/matrix.dart';
import 'package:tatchat/widgets/mxc_image.dart'; import 'package:tatchat/widgets/mxc_image.dart';
import 'package:tatchat/widgets/share_scaffold_dialog.dart';
const _muralMediaTypes = { const _muralMediaTypes = {
MessageTypes.Image, MessageTypes.Image,
@@ -654,9 +656,16 @@ class _PostViewer extends StatefulWidget {
State<_PostViewer> createState() => _PostViewerState(); State<_PostViewer> createState() => _PostViewerState();
} }
class _CommentNode {
final Event event;
final List<_CommentNode> replies;
_CommentNode(this.event) : replies = [];
}
class _PostViewerState extends State<_PostViewer> { class _PostViewerState extends State<_PostViewer> {
final TextEditingController _commentController = TextEditingController(); final TextEditingController _commentController = TextEditingController();
bool _sending = false; bool _sending = false;
Event? _replyingTo;
Event get event => widget.event; Event get event => widget.event;
@@ -702,6 +711,123 @@ class _PostViewerState extends State<_PostViewer> {
} }
} }
Future<void> _forwardPost() async {
await showScaffoldDialog(
context: context,
builder: (context) => ShareScaffoldDialog(
items: [ContentShareItem(event.content.copy())],
),
);
}
Future<void> _toggleCommentReaction(Event comment, String key) async {
final reactions = comment.aggregatedEvents(
widget.timeline,
RelationshipTypes.reaction,
);
for (final r in reactions) {
if (r.senderId != widget.room.client.userID) continue;
final rKey = r.content
.tryGetMap<String, Object?>('m.relates_to')
?.tryGet<String>('key');
if (rKey == key) {
await r.redactEvent();
return;
}
}
await widget.room.sendReaction(comment.eventId, key);
}
Future<void> _showCommentReactionPicker(Event comment) async {
final theme = Theme.of(context);
await showModalBottomSheet<void>(
context: context,
useSafeArea: true,
backgroundColor: theme.colorScheme.surface,
builder: (context) => SizedBox(
height: MediaQuery.sizeOf(context).height * 0.5,
child: EmojiPicker(
onEmojiSelected: (_, emoji) {
Navigator.of(context).pop();
_toggleCommentReaction(comment, emoji.emoji);
},
config: Config(
locale: Localizations.localeOf(context),
emojiViewConfig: EmojiViewConfig(
backgroundColor: theme.colorScheme.onInverseSurface,
),
bottomActionBarConfig: const BottomActionBarConfig(enabled: false),
categoryViewConfig: CategoryViewConfig(
backspaceColor: theme.colorScheme.primary,
iconColor: theme.colorScheme.primary.withAlpha(128),
iconColorSelected: theme.colorScheme.primary,
indicatorColor: theme.colorScheme.primary,
backgroundColor: theme.colorScheme.surface,
),
skinToneConfig: const SkinToneConfig(indicatorColor: Colors.grey),
),
),
),
);
}
List<_CommentNode> _buildCommentTree() {
final allComments = widget.timeline.events
.where(
(e) =>
!e.redacted &&
e.relationshipType == RelationshipTypes.thread &&
e.relationshipEventId == event.eventId,
)
.toList();
allComments.sort(
(a, b) => a.originServerTs.compareTo(b.originServerTs),
);
final nodeMap = <String, _CommentNode>{};
final roots = <_CommentNode>[];
for (final comment in allComments) {
nodeMap[comment.eventId] = _CommentNode(comment);
}
String? resolveRoot(String? id) {
var current = id;
final visited = <String>{};
while (current != null &&
current != event.eventId &&
nodeMap.containsKey(current)) {
if (!visited.add(current)) break;
final node = nodeMap[current]!;
final parentId =
node.event.inReplyToEventId(includingFallback: false);
if (parentId == null ||
parentId == event.eventId ||
!nodeMap.containsKey(parentId)) {
return current;
}
current = parentId;
}
return null;
}
for (final comment in allComments) {
final replyToId = comment.inReplyToEventId(includingFallback: false);
final node = nodeMap[comment.eventId]!;
if (replyToId != null &&
replyToId != event.eventId &&
nodeMap.containsKey(replyToId)) {
final rootId = resolveRoot(replyToId);
if (rootId != null && rootId != comment.eventId) {
nodeMap[rootId]!.replies.add(node);
continue;
}
}
roots.add(node);
}
return roots;
}
Future<void> _editCaption() async { Future<void> _editCaption() async {
final l10n = L10n.of(context); final l10n = L10n.of(context);
final currentBody = event.getDisplayEvent(widget.timeline).body.trim(); final currentBody = event.getDisplayEvent(widget.timeline).body.trim();
@@ -766,13 +892,62 @@ class _PostViewerState extends State<_PostViewer> {
).showSnackBar(SnackBar(content: Text(l10n.contentHasBeenReported))); ).showSnackBar(SnackBar(content: Text(l10n.contentHasBeenReported)));
} }
Future<void> _deleteComment(Event comment) async {
final l10n = L10n.of(context);
final delete = await showOkCancelAlertDialog(
context: context,
title: l10n.areYouSure,
message: l10n.areYouSureDeletePost,
okLabel: l10n.delete,
cancelLabel: l10n.cancel,
);
if (delete != OkCancelResult.ok || !mounted) return;
try {
await comment.redactEvent();
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(L10n.of(context).oopsSomethingWentWrong)),
);
}
}
Future<void> _reportComment(Event comment) async {
final l10n = L10n.of(context);
final reason = await showTextInputDialog(
context: context,
title: l10n.whyDoYouWantToReportThis,
okLabel: l10n.report,
cancelLabel: l10n.cancel,
hintText: l10n.reason,
);
if (reason == null || reason.isEmpty || !mounted) return;
await widget.room.client.reportEvent(
widget.room.id,
comment.eventId,
reason: reason,
);
if (!mounted) return;
ScaffoldMessenger.of(
context,
).showSnackBar(SnackBar(content: Text(l10n.contentHasBeenReported)));
}
Future<void> _sendComment() async { Future<void> _sendComment() async {
final text = _commentController.text.trim(); final text = _commentController.text.trim();
if (text.isEmpty || _sending) return; if (text.isEmpty || _sending) return;
_commentController.clear(); _commentController.clear();
setState(() => _sending = true); final replyTo = _replyingTo;
setState(() {
_sending = true;
_replyingTo = null;
});
try { try {
await widget.room.sendTextEvent(text, threadRootEventId: event.eventId); await widget.room.sendTextEvent(
text,
threadRootEventId: event.eventId,
inReplyTo: replyTo,
);
} finally { } finally {
if (mounted) setState(() => _sending = false); if (mounted) setState(() => _sending = false);
} }
@@ -858,19 +1033,6 @@ class _PostViewerState extends State<_PostViewer> {
); );
} }
List<Event> _comments() {
final comments = widget.timeline.events
.where(
(e) =>
!e.redacted &&
e.relationshipType == RelationshipTypes.thread &&
e.relationshipEventId == event.eventId,
)
.toList();
comments.sort((a, b) => a.originServerTs.compareTo(b.originServerTs));
return comments;
}
void _openProfile(Event comment) { void _openProfile(Event comment) {
final sender = comment.senderFromMemoryOrFallback; final sender = comment.senderFromMemoryOrFallback;
UserDialog.show( UserDialog.show(
@@ -889,12 +1051,18 @@ class _PostViewerState extends State<_PostViewer> {
final l10n = L10n.of(context); final l10n = L10n.of(context);
final isVideo = event.messageType == MessageTypes.Video; final isVideo = event.messageType == MessageTypes.Video;
final imageInfo = event.content.tryGetMap<String, Object?>('info');
final imgW = imageInfo?.tryGet<num>('w')?.toDouble();
final imgH = imageInfo?.tryGet<num>('h')?.toDouble();
final hasImageDimensions =
imgW != null && imgH != null && imgW > 0 && imgH > 0;
return StreamBuilder( return StreamBuilder(
stream: widget.room.client.onSync.stream stream: widget.room.client.onSync.stream
.where((syncUpdate) => syncUpdate.hasRoomUpdate) .where((syncUpdate) => syncUpdate.hasRoomUpdate)
.rateLimit(const Duration(seconds: 1)), .rateLimit(const Duration(seconds: 1)),
builder: (context, snapshots) { builder: (context, snapshots) {
final comments = _comments(); final commentTree = _buildCommentTree();
final caption = event.getDisplayEvent(widget.timeline).body.trim(); final caption = event.getDisplayEvent(widget.timeline).body.trim();
final liked = _myReaction() != null; final liked = _myReaction() != null;
@@ -939,181 +1107,174 @@ class _PostViewerState extends State<_PostViewer> {
), ),
), ),
Expanded( Expanded(
flex: 5, child: ListView(
child: Container( padding: EdgeInsets.zero,
color: theme.colorScheme.surfaceContainerHighest,
width: double.infinity,
child: isVideo
? EventVideoPlayer(event)
: MxcImage(
key: ValueKey(event.eventId),
event: event,
fit: BoxFit.contain,
isThumbnail: false,
),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [ children: [
IconButton( Container(
tooltip: liked ? l10n.unlike : l10n.like, color: theme.colorScheme.surfaceContainerHighest,
icon: Icon( width: double.infinity,
liked ? Icons.favorite : Icons.favorite_border, child: isVideo
color: liked ? Colors.redAccent : null, ? EventVideoPlayer(event)
), : hasImageDimensions
onPressed: _toggleLike, ? AspectRatio(
aspectRatio: imgW / imgH,
child: MxcImage(
key: ValueKey(event.eventId),
event: event,
fit: BoxFit.cover,
isThumbnail: false,
),
)
: MxcImage(
key: ValueKey(event.eventId),
event: event,
width: double.infinity,
fit: BoxFit.contain,
isThumbnail: false,
),
), ),
if (_likeCount > 0) Padding(
padding: const EdgeInsets.symmetric(horizontal: 4),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
IconButton(
tooltip: liked ? l10n.unlike : l10n.like,
icon: Icon(
liked ? Icons.favorite : Icons.favorite_border,
color: liked ? Colors.redAccent : null,
),
onPressed: _toggleLike,
),
if (_likeCount > 0)
Padding(
padding: const EdgeInsets.only(bottom: 10),
child: Text(
'$_likeCount',
style: theme.textTheme.bodyMedium?.copyWith(
color: theme.colorScheme.onSurfaceVariant,
),
),
),
IconButton(
tooltip: l10n.forward,
icon: Icon(Icons.adaptive.share_outlined),
onPressed: _forwardPost,
),
const Spacer(),
PopupMenuButton<String>(
tooltip: l10n.more,
onSelected: (action) async {
switch (action) {
case 'edit':
await _editCaption();
case 'delete':
await _deletePost();
case 'report':
await _reportPost();
}
},
itemBuilder: (context) => [
PopupMenuItem(
value: 'edit',
child: ListTile(
leading: const Icon(Icons.edit_outlined),
title: Text(l10n.editCaption),
contentPadding: EdgeInsets.zero,
),
),
PopupMenuItem(
value: 'delete',
child: ListTile(
leading: const Icon(Icons.delete_outline),
title: Text(l10n.delete),
contentPadding: EdgeInsets.zero,
),
),
PopupMenuItem(
value: 'report',
child: ListTile(
leading: const Icon(Icons.flag_outlined),
title: Text(l10n.report),
contentPadding: EdgeInsets.zero,
),
),
],
icon: const Icon(Icons.more_vert),
),
],
),
),
if (caption.trim().isNotEmpty)
Padding( Padding(
padding: const EdgeInsets.only(bottom: 10), padding: const EdgeInsets.fromLTRB(16, 0, 16, 8),
child: Text( child: Text(
'$_likeCount', caption.trim(),
style: theme.textTheme.bodyMedium?.copyWith( style: theme.textTheme.bodyMedium,
),
),
const Divider(height: 1),
if (commentTree.isEmpty)
Padding(
padding: const EdgeInsets.all(16),
child: Text(
l10n.noCommentsYet,
textAlign: TextAlign.center,
style: TextStyle(
color: theme.colorScheme.onSurfaceVariant, color: theme.colorScheme.onSurfaceVariant,
), ),
), ),
), )
const Spacer(), else
PopupMenuButton<String>( ...commentTree.map((node) => _CommentTile(
tooltip: l10n.more, node: node,
onSelected: (action) async { depth: 0,
switch (action) { timeline: widget.timeline,
case 'edit': currentUserId: widget.room.client.userID,
await _editCaption(); onReply: (comment) => setState(
case 'delete': () => _replyingTo = comment,
await _deletePost(); ),
case 'report': onToggleReaction: _toggleCommentReaction,
await _reportPost(); onShowReactionPicker: _showCommentReactionPicker,
} onOpenProfile: _openProfile,
}, onDelete: _deleteComment,
itemBuilder: (context) => [ onReport: _reportComment,
PopupMenuItem( )),
value: 'edit',
child: ListTile(
leading: const Icon(Icons.edit_outlined),
title: Text(l10n.editCaption),
contentPadding: EdgeInsets.zero,
),
),
PopupMenuItem(
value: 'delete',
child: ListTile(
leading: const Icon(Icons.delete_outline),
title: Text(l10n.delete),
contentPadding: EdgeInsets.zero,
),
),
PopupMenuItem(
value: 'report',
child: ListTile(
leading: const Icon(Icons.flag_outlined),
title: Text(l10n.report),
contentPadding: EdgeInsets.zero,
),
),
],
icon: const Icon(Icons.more_vert),
),
], ],
), ),
), ),
if (caption.trim().isNotEmpty) if (_replyingTo != null)
Padding( Container(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 8), color: theme.colorScheme.secondaryContainer.withValues(
child: Text( alpha: 0.5,
caption.trim(),
style: theme.textTheme.bodyMedium,
), ),
), padding: const EdgeInsets.fromLTRB(16, 6, 8, 6),
const Divider(height: 1), child: Row(
Expanded( children: [
flex: 3, Icon(
child: comments.isEmpty Icons.reply,
? Center( size: 16,
child: Padding( color: theme.colorScheme.primary,
padding: const EdgeInsets.all(16), ),
child: Text( const SizedBox(width: 4),
l10n.noCommentsYet, Expanded(
textAlign: TextAlign.center, child: Text(
style: TextStyle( '${l10n.reply} ${_replyingTo!.senderFromMemoryOrFallback.calcDisplayname()}',
color: theme.colorScheme.onSurfaceVariant, maxLines: 1,
), overflow: TextOverflow.ellipsis,
style: theme.textTheme.bodySmall?.copyWith(
color: theme.colorScheme.primary,
), ),
), ),
)
: ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 4),
itemCount: comments.length,
itemBuilder: (context, i) {
final comment = comments[i];
final isSticker =
comment.messageType == MessageTypes.Sticker;
final sender = comment.senderFromMemoryOrFallback;
return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 6,
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () => _openProfile(comment),
child: Avatar(
mxContent: sender.avatarUrl,
name: sender.calcDisplayname(),
size: 32,
client: widget.room.client,
),
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () => _openProfile(comment),
child: Text(
sender.calcDisplayname(),
style: theme.textTheme.labelMedium
?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 2),
if (isSticker)
MxcImage(
key: ValueKey(comment.eventId),
event: comment,
fit: BoxFit.contain,
isThumbnail: false,
width: double.infinity,
)
else
Text(
comment
.getDisplayEvent(widget.timeline)
.calcLocalizedBodyFallback(
MatrixLocals(l10n),
withSenderNamePrefix: false,
hideReply: true,
),
style: theme.textTheme.bodyMedium,
),
],
),
),
],
),
);
},
), ),
), IconButton(
visualDensity: VisualDensity.compact,
icon: const Icon(Icons.close, size: 18),
onPressed: () => setState(() => _replyingTo = null),
),
],
),
),
Padding( Padding(
padding: const EdgeInsets.fromLTRB(8, 4, 8, 8), padding: const EdgeInsets.fromLTRB(8, 4, 8, 8),
child: Row( child: Row(
@@ -1134,7 +1295,9 @@ class _PostViewerState extends State<_PostViewer> {
textInputAction: TextInputAction.send, textInputAction: TextInputAction.send,
onSubmitted: (_) => _sendComment(), onSubmitted: (_) => _sendComment(),
decoration: InputDecoration( decoration: InputDecoration(
hintText: l10n.optionalMessage, hintText: _replyingTo != null
? '${l10n.reply} ${_replyingTo!.senderFromMemoryOrFallback.calcDisplayname()}'
: l10n.optionalMessage,
isDense: true, isDense: true,
border: InputBorder.none, border: InputBorder.none,
), ),
@@ -1155,3 +1318,248 @@ class _PostViewerState extends State<_PostViewer> {
); );
} }
} }
class _CommentTile extends StatelessWidget {
final _CommentNode node;
final int depth;
final Timeline timeline;
final String? currentUserId;
final ValueChanged<Event> onReply;
final void Function(Event comment, String key) onToggleReaction;
final ValueChanged<Event> onShowReactionPicker;
final ValueChanged<Event> onOpenProfile;
final ValueChanged<Event> onDelete;
final ValueChanged<Event> onReport;
const _CommentTile({
required this.node,
required this.depth,
required this.timeline,
required this.currentUserId,
required this.onReply,
required this.onToggleReaction,
required this.onShowReactionPicker,
required this.onOpenProfile,
required this.onDelete,
required this.onReport,
});
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
final l10n = L10n.of(context);
final comment = node.event;
final isSticker = comment.messageType == MessageTypes.Sticker;
final sender = comment.senderFromMemoryOrFallback;
final indent = depth * 32.0;
final reactions = comment.aggregatedEvents(
timeline,
RelationshipTypes.reaction,
);
final reactionCounts = <String, int>{};
for (final r in reactions) {
final key = r.content
.tryGetMap<String, Object?>('m.relates_to')
?.tryGet<String>('key');
if (key != null) {
reactionCounts[key] = (reactionCounts[key] ?? 0) + 1;
}
}
final myUserId = comment.room.client.userID;
return Padding(
padding: EdgeInsets.only(
left: 16.0 + indent,
right: 16,
top: depth == 0 ? 8 : 2,
bottom: 2,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () => onOpenProfile(comment),
child: Avatar(
mxContent: sender.avatarUrl,
name: sender.calcDisplayname(),
size: depth == 0 ? 32 : 24,
client: comment.room.client,
),
),
const SizedBox(width: 8),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
GestureDetector(
onTap: () => onOpenProfile(comment),
child: Text(
sender.calcDisplayname(),
style: theme.textTheme.labelMedium?.copyWith(
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 2),
if (isSticker)
MxcImage(
key: ValueKey(comment.eventId),
event: comment,
fit: BoxFit.contain,
isThumbnail: false,
width: 200,
)
else
Text(
comment
.getDisplayEvent(timeline)
.calcLocalizedBodyFallback(
MatrixLocals(l10n),
withSenderNamePrefix: false,
hideReply: true,
),
style: theme.textTheme.bodyMedium,
),
const SizedBox(height: 4),
Row(
children: [
if (depth == 0)
GestureDetector(
onTap: () => onReply(comment),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 2),
child: Text(
l10n.reply,
style: theme.textTheme.labelSmall?.copyWith(
color: theme.colorScheme.primary,
fontWeight: FontWeight.w500,
),
),
),
),
if (depth == 0) const SizedBox(width: 12),
...reactionCounts.entries.map((entry) {
final isMyReaction = reactions.any(
(r) =>
r.senderId == myUserId &&
r.content
.tryGetMap<String, Object?>(
'm.relates_to',
)
?.tryGet<String>('key') ==
entry.key,
);
return Padding(
padding: const EdgeInsets.only(right: 4),
child: GestureDetector(
onTap: () =>
onToggleReaction(comment, entry.key),
child: Container(
padding: const EdgeInsets.symmetric(
horizontal: 6,
vertical: 2,
),
decoration: BoxDecoration(
color: isMyReaction
? theme.colorScheme.primaryContainer
: theme
.colorScheme
.surfaceContainerHighest,
borderRadius: BorderRadius.circular(12),
border: isMyReaction
? Border.all(
color:
theme.colorScheme.primary,
width: 1,
)
: null,
),
child: Text(
'${entry.key} ${entry.value}',
style: theme.textTheme.labelSmall,
),
),
),
);
}),
GestureDetector(
onTap: () => onShowReactionPicker(comment),
child: Padding(
padding: const EdgeInsets.all(2),
child: Icon(
Icons.add_reaction_outlined,
size: 18,
color: theme.colorScheme.onSurfaceVariant,
),
),
),
const Spacer(),
PopupMenuButton<String>(
tooltip: l10n.more,
padding: EdgeInsets.zero,
color: theme.colorScheme.surface,
onSelected: (action) async {
switch (action) {
case 'delete':
onDelete(comment);
case 'report':
onReport(comment);
}
},
itemBuilder: (context) => [
if (comment.senderId == currentUserId)
PopupMenuItem(
value: 'delete',
child: ListTile(
leading: const Icon(Icons.delete_outline),
title: Text(l10n.deleteMessage),
contentPadding: EdgeInsets.zero,
),
),
PopupMenuItem(
value: 'report',
child: ListTile(
leading: const Icon(Icons.flag_outlined),
title: Text(l10n.reportMessage),
contentPadding: EdgeInsets.zero,
),
),
],
icon: Icon(
Icons.more_vert,
size: 18,
color: theme.colorScheme.onSurfaceVariant,
),
),
],
),
],
),
),
],
),
...node.replies.map(
(reply) => _CommentTile(
node: reply,
depth: depth + 1,
timeline: timeline,
currentUserId: currentUserId,
onReply: onReply,
onToggleReaction: onToggleReaction,
onShowReactionPicker: onShowReactionPicker,
onOpenProfile: onOpenProfile,
onDelete: onDelete,
onReport: onReport,
),
),
],
),
);
}
}

View File

@@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake)
# https://github.com/flutter/flutter/issues/57146. # https://github.com/flutter/flutter/issues/57146.
set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
# Set fallback configurations for older versions of the flutter tool.
if (NOT DEFINED FLUTTER_TARGET_PLATFORM)
set(FLUTTER_TARGET_PLATFORM "windows-x64")
endif()
# === Flutter Library === # === Flutter Library ===
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
@@ -92,7 +97,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E env COMMAND ${CMAKE_COMMAND} -E env
${FLUTTER_TOOL_ENVIRONMENT} ${FLUTTER_TOOL_ENVIRONMENT}
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
windows-x64 $<CONFIG> ${FLUTTER_TARGET_PLATFORM} $<CONFIG>
VERBATIM VERBATIM
) )
add_custom_target(flutter_assemble DEPENDS add_custom_target(flutter_assemble DEPENDS

View File

@@ -20,6 +20,13 @@ add_executable(${BINARY_NAME} WIN32
# that need different build settings. # that need different build settings.
apply_standard_settings(${BINARY_NAME}) apply_standard_settings(${BINARY_NAME})
# Add preprocessor definitions for the build version.
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}")
target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}")
# Disable Windows macros that collide with C++ standard library functions. # Disable Windows macros that collide with C++ standard library functions.
target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")

View File

@@ -60,14 +60,14 @@ IDI_APP_ICON ICON "resources\\app_icon.ico"
// Version // Version
// //
#ifdef FLUTTER_BUILD_NUMBER #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)
#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD
#else #else
#define VERSION_AS_NUMBER 1,0,0 #define VERSION_AS_NUMBER 1,0,0,0
#endif #endif
#ifdef FLUTTER_BUILD_NAME #if defined(FLUTTER_VERSION)
#define VERSION_AS_STRING #FLUTTER_BUILD_NAME #define VERSION_AS_STRING FLUTTER_VERSION
#else #else
#define VERSION_AS_STRING "1.0.0" #define VERSION_AS_STRING "1.0.0"
#endif #endif