258 lines
9.4 KiB
Dart
258 lines
9.4 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:nextcloud/nextcloud.dart';
|
|
import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart';
|
|
|
|
import '../../../../api/marianumcloud/files-sharing/fileSharingApi.dart';
|
|
import '../../../../api/marianumcloud/files-sharing/fileSharingApiParams.dart';
|
|
import '../../../../api/marianumcloud/talk/sendMessage/sendMessage.dart';
|
|
import '../../../../api/marianumcloud/talk/sendMessage/sendMessageParams.dart';
|
|
import '../../../../api/marianumcloud/webdav/webdavApi.dart';
|
|
import '../../../../state/app/modules/chat/bloc/chat_bloc.dart';
|
|
import '../../../../state/app/modules/settings/bloc/settings_cubit.dart';
|
|
import '../../../../widget/async_action_button.dart';
|
|
import '../../../../widget/file_pick.dart';
|
|
import '../../../../widget/focus_behaviour.dart';
|
|
import '../../files/files_upload_dialog.dart';
|
|
import 'answer_reference.dart';
|
|
|
|
class ChatTextfield extends StatefulWidget {
|
|
final String sendToToken;
|
|
final String? selfId;
|
|
|
|
const ChatTextfield(this.sendToToken, {this.selfId, super.key});
|
|
|
|
@override
|
|
State<ChatTextfield> createState() => _ChatTextfieldState();
|
|
}
|
|
|
|
class _ChatTextfieldState extends State<ChatTextfield> {
|
|
late SettingsCubit settings;
|
|
final TextEditingController _textBoxController = TextEditingController();
|
|
final AsyncActionController _sendController = AsyncActionController();
|
|
String? _sendError;
|
|
|
|
void share(String shareFolder, List<String> filePaths) {
|
|
for (final element in filePaths) {
|
|
final fileName = element.split(Platform.pathSeparator).last;
|
|
FileSharingApi().share(FileSharingApiParams(
|
|
shareType: 10,
|
|
shareWith: widget.sendToToken,
|
|
path: '$shareFolder/$fileName',
|
|
)).then((_) {
|
|
if (mounted) context.read<ChatBloc>().refresh();
|
|
});
|
|
}
|
|
}
|
|
|
|
Future<void> mediaUpload(List<String>? paths) async {
|
|
if (paths == null) return;
|
|
|
|
const shareFolder = 'MarianumMobile';
|
|
WebdavApi.webdav.then((webdav) => webdav.mkcol(PathUri.parse('/$shareFolder')));
|
|
|
|
if (!mounted) return;
|
|
pushScreen(
|
|
context,
|
|
withNavBar: false,
|
|
screen: FilesUploadDialog(
|
|
filePaths: paths,
|
|
remotePath: shareFolder,
|
|
onUploadFinished: (uploaded) => share(shareFolder, uploaded),
|
|
uniqueNames: true,
|
|
),
|
|
);
|
|
}
|
|
|
|
void _setDraft(String text) {
|
|
final talkSettings = settings.val(write: true).talkSettings;
|
|
if (text.isNotEmpty) {
|
|
talkSettings.drafts[widget.sendToToken] = text;
|
|
} else {
|
|
talkSettings.drafts.removeWhere((key, _) => key == widget.sendToToken);
|
|
}
|
|
}
|
|
|
|
void _setDraftReply(int? messageId) {
|
|
final talkSettings = settings.val(write: true).talkSettings;
|
|
if (messageId != null) {
|
|
talkSettings.draftReplies[widget.sendToToken] = messageId;
|
|
} else {
|
|
talkSettings.draftReplies.removeWhere((key, _) => key == widget.sendToToken);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
settings = context.read<SettingsCubit>();
|
|
final draftReply = settings.val().talkSettings.draftReplies[widget.sendToToken];
|
|
if (draftReply != null) {
|
|
context.read<ChatBloc>().setReferenceMessageId(draftReply);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_sendController.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
Future<void> _sendMessage(ChatBloc chatBloc) async {
|
|
if (_textBoxController.text.isEmpty) return;
|
|
final text = _textBoxController.text;
|
|
final replyTo = chatBloc.state.data?.referenceMessageId?.toString();
|
|
setState(() => _sendError = null);
|
|
await SendMessage(
|
|
widget.sendToToken,
|
|
SendMessageParams(text, replyTo: replyTo),
|
|
).run();
|
|
if (!mounted) return;
|
|
chatBloc.refresh();
|
|
_textBoxController.text = '';
|
|
_setDraft('');
|
|
chatBloc.setReferenceMessageId(null);
|
|
_setDraftReply(null);
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
_textBoxController.text = settings.val().talkSettings.drafts[widget.sendToToken] ?? '';
|
|
final chatBloc = context.watch<ChatBloc>();
|
|
final chatState = chatBloc.state.data;
|
|
|
|
Widget replyBanner = const SizedBox.shrink();
|
|
if (chatState != null && chatState.referenceMessageId != null && chatState.chatResponse != null) {
|
|
try {
|
|
final referenceMessage = chatState.chatResponse!.sortByTimestamp().firstWhere(
|
|
(e) => e.id == chatState.referenceMessageId,
|
|
);
|
|
replyBanner = Row(
|
|
children: [
|
|
Expanded(
|
|
child: AnswerReference(
|
|
context: context,
|
|
referenceMessage: referenceMessage,
|
|
selfId: widget.selfId,
|
|
),
|
|
),
|
|
IconButton(
|
|
onPressed: () {
|
|
chatBloc.setReferenceMessageId(null);
|
|
_setDraftReply(null);
|
|
},
|
|
icon: const Icon(Icons.close_outlined),
|
|
padding: const EdgeInsets.only(left: 0),
|
|
),
|
|
],
|
|
);
|
|
} catch (_) {/* reference no longer in current chat data */}
|
|
}
|
|
|
|
return Stack(children: <Widget>[
|
|
Align(
|
|
alignment: Alignment.bottomLeft,
|
|
child: Container(
|
|
padding: const EdgeInsets.only(left: 10, bottom: 3, top: 3, right: 10),
|
|
width: double.infinity,
|
|
child: Column(
|
|
children: [
|
|
replyBanner,
|
|
if (_sendError != null)
|
|
Padding(
|
|
padding: const EdgeInsets.only(bottom: 4),
|
|
child: Text(
|
|
_sendError!,
|
|
style: TextStyle(color: Theme.of(context).colorScheme.error, fontSize: 12),
|
|
),
|
|
),
|
|
Row(children: <Widget>[
|
|
GestureDetector(
|
|
onTap: () {
|
|
showDialog(context: context, builder: (dialogCtx) => SimpleDialog(children: [
|
|
ListTile(
|
|
leading: const Icon(Icons.file_open),
|
|
title: const Text('Aus Dateien auswählen'),
|
|
onTap: () {
|
|
FilePick.documentPick().then(mediaUpload);
|
|
Navigator.of(dialogCtx).pop();
|
|
},
|
|
),
|
|
Visibility(
|
|
visible: !Platform.isIOS,
|
|
child: ListTile(
|
|
leading: const Icon(Icons.image),
|
|
title: const Text('Aus Gallerie auswählen'),
|
|
onTap: () {
|
|
FilePick.multipleGalleryPick().then((value) {
|
|
if (value != null) mediaUpload(value.map((e) => e.path).toList());
|
|
});
|
|
Navigator.of(dialogCtx).pop();
|
|
},
|
|
),
|
|
),
|
|
]));
|
|
},
|
|
child: Material(
|
|
elevation: 5,
|
|
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
|
|
child: Container(
|
|
height: 30,
|
|
width: 30,
|
|
decoration: BoxDecoration(
|
|
color: Theme.of(context).primaryColor,
|
|
borderRadius: BorderRadius.circular(30),
|
|
),
|
|
child: const Icon(Icons.attach_file_outlined, color: Colors.white, size: 20),
|
|
),
|
|
),
|
|
),
|
|
const SizedBox(width: 15),
|
|
Expanded(
|
|
child: TextField(
|
|
autocorrect: true,
|
|
textCapitalization: TextCapitalization.sentences,
|
|
controller: _textBoxController,
|
|
maxLines: 7,
|
|
minLines: 1,
|
|
decoration: const InputDecoration(
|
|
hintText: 'Nachricht schreiben...',
|
|
border: InputBorder.none,
|
|
),
|
|
onChanged: (text) {
|
|
if (text.trim().toLowerCase() == 'marbot marbot marbot') {
|
|
const newText = 'Roboter sind cool und so, aber Marbots sind besser!';
|
|
_textBoxController.text = newText;
|
|
text = newText;
|
|
}
|
|
_setDraft(text);
|
|
},
|
|
onTapOutside: (_) => FocusBehaviour.textFieldTapOutside(context),
|
|
),
|
|
),
|
|
const SizedBox(width: 15),
|
|
ValueListenableBuilder<TextEditingValue>(
|
|
valueListenable: _textBoxController,
|
|
builder: (context, value, _) => AsyncFab(
|
|
mini: true,
|
|
heroTag: 'chatSend_${widget.sendToToken}',
|
|
icon: Icons.send,
|
|
backgroundColor: Theme.of(context).primaryColor,
|
|
foregroundColor: Colors.white,
|
|
controller: _sendController,
|
|
onPressed: value.text.trim().isEmpty ? null : () => _sendMessage(chatBloc),
|
|
onError: (message) => setState(() => _sendError = message),
|
|
onSuccess: () => setState(() => _sendError = null),
|
|
),
|
|
),
|
|
]),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
]);
|
|
}
|
|
}
|