Split attachment list into separate file

This commit is contained in:
Nutcake 2023-05-18 12:18:53 +02:00
parent 52d6f40d82
commit 0e15b3c387
2 changed files with 160 additions and 103 deletions

View file

@ -0,0 +1,140 @@
import 'dart:io';
import 'package:contacts_plus_plus/widgets/messages/message_camera_view.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
import 'package:path/path.dart';
class MessageAttachmentList extends StatefulWidget {
const MessageAttachmentList({required this.onChange, required this.disabled, this.initialFiles, super.key});
final List<File>? initialFiles;
final Function(List<File> files) onChange;
final bool disabled;
@override
State<MessageAttachmentList> createState() => _MessageAttachmentListState();
}
class _MessageAttachmentListState extends State<MessageAttachmentList> {
final List<File> _loadedFiles = [];
final ScrollController _scrollController = ScrollController();
bool _showShadow = true;
@override
void initState() {
super.initState();
_loadedFiles.clear();
_loadedFiles.addAll(widget.initialFiles ?? []);
_scrollController.addListener(() {
if (_scrollController.position.maxScrollExtent > 0 && !_showShadow) {
setState(() {
_showShadow = true;
});
}
if (_scrollController.position.atEdge && _scrollController.position.pixels > 0
&& _showShadow) {
setState(() {
_showShadow = false;
});
}
});
}
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.max,
children: [
Expanded(
child: ShaderMask(
shaderCallback: (Rect bounds) {
return LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.transparent, Colors.transparent, Colors.transparent, Theme.of(context).colorScheme.background],
stops: [0.0, 0.0, _showShadow ? 0.96 : 1.0, 1.0], // 10% purple, 80% transparent, 10% purple
).createShader(bounds);
},
blendMode: BlendMode.dstOut,
child: SingleChildScrollView(
controller: _scrollController,
scrollDirection: Axis.horizontal,
child: Row(
children: _loadedFiles.map((file) =>
Padding(
padding: const EdgeInsets.only(left: 4.0, right: 4.0, top: 4.0),
child: TextButton.icon(
onPressed: widget.disabled ? null : () {
showDialog(context: context, builder: (context) =>
AlertDialog(
title: const Text("Remove attachment"),
content: Text(
"This will remove attachment '${basename(
file.path)}', are you sure?"),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("No"),
),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
_loadedFiles.remove(file);
await widget.onChange(_loadedFiles);
},
child: const Text("Yes"),
)
],
),
);
},
style: TextButton.styleFrom(
foregroundColor: Theme
.of(context)
.colorScheme
.onBackground,
side: BorderSide(
color: Theme
.of(context)
.colorScheme
.primary,
width: 1
),
),
label: Text(basename(file.path)),
icon: const Icon(Icons.attach_file),
),
),
).toList()
),
),
),
),
IconButton(
onPressed: widget.disabled ? null : () async {
final result = await FilePicker.platform.pickFiles(type: FileType.image);
if (result != null && result.files.single.path != null) {
_loadedFiles.add(File(result.files.single.path!));
await widget.onChange(_loadedFiles);
}
},
icon: const Icon(Icons.add_photo_alternate),
),
IconButton(
onPressed: widget.disabled ? null : () async {
final picture = await Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const MessageCameraView()));
if (picture != null) {
_loadedFiles.add(picture);
await widget.onChange(_loadedFiles);
}
},
icon: const Icon(Icons.add_a_photo),
),
],
);
}
}

View file

@ -9,11 +9,11 @@ import 'package:contacts_plus_plus/models/friend.dart';
import 'package:contacts_plus_plus/models/message.dart'; import 'package:contacts_plus_plus/models/message.dart';
import 'package:contacts_plus_plus/widgets/default_error_widget.dart'; import 'package:contacts_plus_plus/widgets/default_error_widget.dart';
import 'package:contacts_plus_plus/widgets/friends/friend_online_status_indicator.dart'; import 'package:contacts_plus_plus/widgets/friends/friend_online_status_indicator.dart';
import 'package:contacts_plus_plus/widgets/messages/message_attachment_list.dart';
import 'package:contacts_plus_plus/widgets/messages/message_camera_view.dart'; import 'package:contacts_plus_plus/widgets/messages/message_camera_view.dart';
import 'package:contacts_plus_plus/widgets/messages/messages_session_header.dart'; import 'package:contacts_plus_plus/widgets/messages/messages_session_header.dart';
import 'package:file_picker/file_picker.dart'; import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:path/path.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'message_bubble.dart'; import 'message_bubble.dart';
@ -36,7 +36,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
bool _hasText = false; bool _hasText = false;
bool _isSending = false; bool _isSending = false;
bool _attachmentPickerOpen = false; bool _attachmentPickerOpen = false;
double _sendProgress = 0; double? _sendProgress;
bool _showBottomBarShadow = false; bool _showBottomBarShadow = false;
bool _showSessionListScrollChevron = false; bool _showSessionListScrollChevron = false;
@ -319,108 +319,21 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
], ],
), ),
(false, []) => null, (false, []) => null,
(_, _) => (_, _) => MessageAttachmentList(
Row( disabled: _isSending,
mainAxisSize: MainAxisSize.max, initialFiles: _loadedFiles,
children: [ onChange: (List<File> loadedFiles) => setState(() {
Expanded( _loadedFiles.clear();
child: ShaderMask( _loadedFiles.addAll(loadedFiles);
shaderCallback: (Rect bounds) { }),
return LinearGradient(
begin: Alignment.centerLeft,
end: Alignment.centerRight,
colors: [Colors.transparent, Colors.transparent, Colors.transparent, Theme.of(context).colorScheme.background],
stops: const [0.0, 0.1, 0.9, 1.0], // 10% purple, 80% transparent, 10% purple
).createShader(bounds);
},
blendMode: BlendMode.dstOut,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Row(
children: _loadedFiles.map((file) =>
Padding(
padding: const EdgeInsets.symmetric(horizontal: 4.0),
child: TextButton.icon(
onPressed: _isSending ? null : () {
showDialog(context: context, builder: (context) =>
AlertDialog(
title: const Text("Remove attachment"),
content: Text(
"This will remove attachment '${basename(
file.path)}', are you sure?"),
actions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("No"),
),
TextButton(
onPressed: () {
setState(() {
_loadedFiles.remove(file);
});
Navigator.of(context).pop();
},
child: const Text("Yes"),
) )
],
));
},
style: TextButton.styleFrom(
foregroundColor: Theme
.of(context)
.colorScheme
.onBackground,
side: BorderSide(
color: Theme
.of(context)
.colorScheme
.primary,
width: 1
),
),
label: Text(basename(file.path)),
icon: const Icon(Icons.attach_file),
),
),
).toList()
),
),
),
),
IconButton(
onPressed: _isSending ? null : () async {
final result = await FilePicker.platform.pickFiles(type: FileType.image);
if (result != null && result.files.single.path != null) {
setState(() {
_loadedFiles.add(File(result.files.single.path!));
});
}
},
icon: const Icon(Icons.add_photo_alternate),
),
IconButton(
onPressed: _isSending ? null : () async {
final picture = await Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const MessageCameraView()));
if (picture != null) {
setState(() {
_loadedFiles.add(picture);
});
}
},
icon: const Icon(Icons.add_a_photo),
),
],
),
}, },
), ),
), ),
], ],
), ),
), ),
if (_isSending && _loadedFiles.isNotEmpty) if (_isSending && _sendProgress != null)
Align( Align(
alignment: Alignment.bottomCenter, alignment: Alignment.bottomCenter,
child: LinearProgressIndicator(value: _sendProgress), child: LinearProgressIndicator(value: _sendProgress),
@ -548,14 +461,17 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
splashRadius: 24, splashRadius: 24,
onPressed: _isSending ? null : () async { onPressed: _isSending ? null : () async {
final sMsgnr = ScaffoldMessenger.of(context); final sMsgnr = ScaffoldMessenger.of(context);
final toSend = List.from(_loadedFiles);
setState(() { setState(() {
_isSending = true; _isSending = true;
_sendProgress = 0; _sendProgress = 0;
_attachmentPickerOpen = false;
_loadedFiles.clear();
}); });
try { try {
for (int i = 0; i < _loadedFiles.length; i++) { for (int i = 0; i < toSend.length; i++) {
final totalProgress = i/_loadedFiles.length; final totalProgress = i/toSend.length;
final file = _loadedFiles[i]; final file = toSend[i];
await sendImageMessage(apiClient, mClient, file, ClientHolder await sendImageMessage(apiClient, mClient, file, ClientHolder
.of(context) .of(context)
.settingsClient .settingsClient
@ -564,13 +480,13 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
.valueOrDefault, .valueOrDefault,
(progress) => (progress) =>
setState(() { setState(() {
_sendProgress = totalProgress + progress * 1/_loadedFiles.length; _sendProgress = totalProgress + progress * 1/toSend.length;
}), }),
); );
} }
setState(() { setState(() {
_sendProgress = 1; _sendProgress = null;
}); });
if (_hasText) { if (_hasText) {
@ -585,6 +501,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
} }
setState(() { setState(() {
_isSending = false; _isSending = false;
_sendProgress = null;
}); });
}, },
iconSize: 28, iconSize: 28,