feat: Send optional message with images or files

This commit is contained in:
Krille 2025-02-25 13:44:46 +01:00
commit 4f67992d9e
No known key found for this signature in database
GPG key ID: E067ECD60F1A0652
4 changed files with 131 additions and 56 deletions

View file

@ -3203,5 +3203,6 @@
"crossVerifiedDevices": "Cross verified devices", "crossVerifiedDevices": "Cross verified devices",
"verifiedDevicesOnly": "Verified devices only", "verifiedDevicesOnly": "Verified devices only",
"takeAPhoto": "Take a photo", "takeAPhoto": "Take a photo",
"recordAVideo": "Record a video" "recordAVideo": "Record a video",
"optionalMessage": "(Optional) message..."
} }

View file

@ -13,6 +13,7 @@ import 'package:fluffychat/utils/other_party_can_receive.dart';
import 'package:fluffychat/utils/platform_infos.dart'; import 'package:fluffychat/utils/platform_infos.dart';
import 'package:fluffychat/utils/size_string.dart'; import 'package:fluffychat/utils/size_string.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/dialog_text_field.dart';
import '../../utils/resize_video.dart'; import '../../utils/resize_video.dart';
class SendFileDialog extends StatefulWidget { class SendFileDialog extends StatefulWidget {
@ -37,6 +38,8 @@ class SendFileDialogState extends State<SendFileDialog> {
/// Images smaller than 20kb don't need compression. /// Images smaller than 20kb don't need compression.
static const int minSizeToCompress = 20 * 1000; static const int minSizeToCompress = 20 * 1000;
final TextEditingController _labelTextController = TextEditingController();
Future<void> _send() async { Future<void> _send() async {
final scaffoldMessenger = ScaffoldMessenger.of(widget.outerContext); final scaffoldMessenger = ScaffoldMessenger.of(widget.outerContext);
final l10n = L10n.of(context); final l10n = L10n.of(context);
@ -93,11 +96,14 @@ class SendFileDialogState extends State<SendFileDialog> {
scaffoldMessenger.clearSnackBars(); scaffoldMessenger.clearSnackBars();
} }
final label = _labelTextController.text.trim();
try { try {
await widget.room.sendFileEvent( await widget.room.sendFileEvent(
file, file,
thumbnail: thumbnail, thumbnail: thumbnail,
shrinkImageMaxDimension: compress ? 1600 : null, shrinkImageMaxDimension: compress ? 1600 : null,
extraContent: label.isEmpty ? null : {'body': label},
); );
} on MatrixException catch (e) { } on MatrixException catch (e) {
final retryAfterMs = e.retryAfterMs; final retryAfterMs = e.retryAfterMs;
@ -121,7 +127,8 @@ class SendFileDialogState extends State<SendFileDialog> {
await widget.room.sendFileEvent( await widget.room.sendFileEvent(
file, file,
thumbnail: thumbnail, thumbnail: thumbnail,
shrinkImageMaxDimension: compress ? null : 1600, shrinkImageMaxDimension: compress ? 1600 : null,
extraContent: label.isEmpty ? null : {'body': label},
); );
} }
} }
@ -306,6 +313,18 @@ class SendFileDialogState extends State<SendFileDialog> {
], ],
), ),
), ),
if (widget.files.length == 1)
Padding(
padding: const EdgeInsets.symmetric(vertical: 12.0),
child: DialogTextField(
controller: _labelTextController,
labelText: L10n.of(context).optionalMessage,
minLines: 1,
maxLines: 3,
maxLength: 255,
counterText: '',
),
),
// Workaround for SwitchListTile.adaptive crashes in CupertinoDialog // Workaround for SwitchListTile.adaptive crashes in CupertinoDialog
if ({'image', 'video'}.contains(uniqueFileType)) if ({'image', 'video'}.contains(uniqueFileType))
Row( Row(

View file

@ -0,0 +1,95 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class DialogTextField extends StatelessWidget {
final TextEditingController? controller;
final String? hintText;
final String? labelText;
final String? initialText;
final String? counterText;
final String? prefixText;
final String? suffixText;
final String? errorText;
final bool obscureText = false;
final bool isDestructive = false;
final int? minLines;
final int? maxLines;
final TextInputType? keyboardType;
final int? maxLength;
final bool autocorrect = true;
const DialogTextField({
super.key,
this.hintText,
this.labelText,
this.initialText,
this.prefixText,
this.suffixText,
this.minLines,
this.maxLines,
this.keyboardType,
this.maxLength,
this.controller,
this.counterText,
this.errorText,
});
@override
Widget build(BuildContext context) {
final prefixText = this.prefixText;
final suffixText = this.suffixText;
final errorText = this.errorText;
final theme = Theme.of(context);
switch (theme.platform) {
case TargetPlatform.android:
case TargetPlatform.fuchsia:
case TargetPlatform.linux:
case TargetPlatform.windows:
return TextField(
controller: controller,
obscureText: obscureText,
minLines: minLines,
maxLines: maxLines,
maxLength: maxLength,
keyboardType: keyboardType,
autocorrect: autocorrect,
decoration: InputDecoration(
errorText: errorText,
hintText: hintText,
labelText: labelText,
prefixText: prefixText,
suffixText: suffixText,
counterText: counterText,
),
);
case TargetPlatform.iOS:
case TargetPlatform.macOS:
return Column(
mainAxisSize: MainAxisSize.min,
children: [
CupertinoTextField(
controller: controller,
obscureText: obscureText,
minLines: minLines,
maxLines: maxLines,
maxLength: maxLength,
keyboardType: keyboardType,
autocorrect: autocorrect,
prefix: prefixText != null ? Text(prefixText) : null,
suffix: suffixText != null ? Text(suffixText) : null,
placeholder: labelText ?? hintText,
),
if (errorText != null)
Text(
errorText,
style: TextStyle(
fontSize: 11,
color: theme.colorScheme.error,
),
textAlign: TextAlign.left,
),
],
);
}
}
}

View file

@ -1,4 +1,3 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
@ -6,6 +5,7 @@ import 'package:flutter_linkify/flutter_linkify.dart';
import 'package:fluffychat/utils/url_launcher.dart'; import 'package:fluffychat/utils/url_launcher.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart'; import 'package:fluffychat/widgets/adaptive_dialogs/adaptive_dialog_action.dart';
import 'package:fluffychat/widgets/adaptive_dialogs/dialog_text_field.dart';
Future<String?> showTextInputDialog({ Future<String?> showTextInputDialog({
required BuildContext context, required BuildContext context,
@ -28,7 +28,6 @@ Future<String?> showTextInputDialog({
int? maxLength, int? maxLength,
bool autocorrect = true, bool autocorrect = true,
}) { }) {
final theme = Theme.of(context);
return showAdaptiveDialog<String>( return showAdaptiveDialog<String>(
context: context, context: context,
useRootNavigator: useRootNavigator, useRootNavigator: useRootNavigator,
@ -61,58 +60,19 @@ Future<String?> showTextInputDialog({
ValueListenableBuilder<String?>( ValueListenableBuilder<String?>(
valueListenable: error, valueListenable: error,
builder: (context, error, _) { builder: (context, error, _) {
switch (theme.platform) { return DialogTextField(
case TargetPlatform.android: hintText: hintText,
case TargetPlatform.fuchsia: errorText: error,
case TargetPlatform.linux: labelText: labelText,
case TargetPlatform.windows: controller: controller,
return TextField( initialText: initialText,
controller: controller, prefixText: prefixText,
obscureText: obscureText, suffixText: suffixText,
minLines: minLines, minLines: minLines,
maxLines: maxLines, maxLines: maxLines,
maxLength: maxLength, maxLength: maxLength,
keyboardType: keyboardType, keyboardType: keyboardType,
autocorrect: autocorrect, );
decoration: InputDecoration(
errorText: error,
hintText: hintText,
labelText: labelText,
prefixText: prefixText,
suffixText: suffixText,
),
);
case TargetPlatform.iOS:
case TargetPlatform.macOS:
return Column(
mainAxisSize: MainAxisSize.min,
children: [
CupertinoTextField(
controller: controller,
obscureText: obscureText,
minLines: minLines,
maxLines: maxLines,
maxLength: maxLength,
keyboardType: keyboardType,
autocorrect: autocorrect,
prefix:
prefixText != null ? Text(prefixText) : null,
suffix:
suffixText != null ? Text(suffixText) : null,
placeholder: labelText ?? hintText,
),
if (error != null)
Text(
error,
style: TextStyle(
fontSize: 11,
color: theme.colorScheme.error,
),
textAlign: TextAlign.left,
),
],
);
}
}, },
), ),
], ],