Clean up asset upload functionality

This commit is contained in:
Nutcake 2023-05-20 15:40:03 +02:00
parent b7a27944cf
commit a8063a74d7
6 changed files with 2940 additions and 796 deletions

View file

@ -4,7 +4,7 @@ import 'dart:math';
import 'dart:typed_data';
import 'package:collection/collection.dart';
import 'package:contacts_plus_plus/models/records/asset_digest.dart';
import 'package:contacts_plus_plus/models/records/image_template.dart';
import 'package:contacts_plus_plus/models/records/json_template.dart';
import 'package:http/http.dart' as http;
import 'package:flutter/material.dart';
@ -125,7 +125,7 @@ class RecordApi {
final imageData = await decodeImageFromList(imageDigest.data);
final objectJson = jsonEncode(
ImageTemplate(imageUri: imageDigest.dbUri, width: imageData.width, height: imageData.height).data);
JsonTemplate.image(imageUri: imageDigest.dbUri, width: imageData.width, height: imageData.height).data);
final objectBytes = Uint8List.fromList(utf8.encode(objectJson));
final objectDigest = await AssetDigest.fromData(objectBytes, "${basenameWithoutExtension(image.path)}.json");
@ -183,4 +183,37 @@ class RecordApi {
progressCallback?.call(1);
return record;
}
static Future<Record> uploadRawFile(ApiClient client, {required File file, required String machineId, void Function(double progress)? progressCallback}) async {
progressCallback?.call(0);
final fileDigest = await AssetDigest.fromData(await file.readAsBytes(), basename(file.path));
final objectJson = jsonEncode(JsonTemplate.rawFile(assetUri: fileDigest.dbUri, filename: fileDigest.name).data);
final objectBytes = Uint8List.fromList(utf8.encode(objectJson));
final objectDigest = await AssetDigest.fromData(objectBytes, "${basenameWithoutExtension(file.path)}.json");
final digests = [fileDigest, objectDigest];
final record = Record.fromRequiredData(
recordType: RecordType.texture,
userId: client.userId,
machineId: machineId,
assetUri: objectDigest.dbUri,
filename: fileDigest.name,
thumbnailUri: JsonTemplate.thumbUrl,
digests: digests,
);
progressCallback?.call(.1);
final status = await tryPreprocessRecord(client, record: record);
final toUpload = status.resultDiffs.whereNot((element) => element.isUploaded);
progressCallback?.call(.2);
await uploadAssets(
client,
assets: digests.where((digest) => toUpload.any((diff) => digest.asset.hash == diff.hash)).toList(),
progressCallback: (progress) => progressCallback?.call(.2 + progress * .6));
progressCallback?.call(1);
return record;
}
}

View file

@ -1,757 +0,0 @@
import 'dart:ui';
import 'package:uuid/uuid.dart';
class ImageTemplate {
late final Map data;
ImageTemplate({required String imageUri, required int width, required int height}) {
final texture2dUid = const Uuid().v4();
final quadMeshUid = const Uuid().v4();
final quadMeshSizeUid = const Uuid().v4();
final materialId = const Uuid().v4();
final boxColliderSizeUid = const Uuid().v4();
data = {
"Object": {
"ID": const Uuid().v4(),
"Components": {
"ID": const Uuid().v4(),
"Data": [
{
"Type": "FrooxEngine.Grabbable",
"Data": {
"ID": const Uuid().v4(),
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 0
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"ReparentOnRelease": {
"ID": const Uuid().v4(),
"Data": true
},
"PreserveUserSpace": {
"ID": const Uuid().v4(),
"Data": true
},
"DestroyOnRelease": {
"ID": const Uuid().v4(),
"Data": false
},
"GrabPriority": {
"ID": const Uuid().v4(),
"Data": 0
},
"GrabPriorityWhenGrabbed": {
"ID": const Uuid().v4(),
"Data": null
},
"CustomCanGrabCheck": {
"ID": const Uuid().v4(),
"Data": {
"Target": null
}
},
"EditModeOnly": {
"ID": const Uuid().v4(),
"Data": false
},
"AllowSteal": {
"ID": const Uuid().v4(),
"Data": false
},
"DropOnDisable": {
"ID": const Uuid().v4(),
"Data": true
},
"ActiveUserFilter": {
"ID": const Uuid().v4(),
"Data": "Disabled"
},
"OnlyUsers": {
"ID": const Uuid().v4(),
"Data": []
},
"Scalable": {
"ID": const Uuid().v4(),
"Data": true
},
"Receivable": {
"ID": const Uuid().v4(),
"Data": true
},
"AllowOnlyPhysicalGrab": {
"ID": const Uuid().v4(),
"Data": false
},
"_grabber": {
"ID": const Uuid().v4(),
"Data": null
},
"_lastParent": {
"ID": const Uuid().v4(),
"Data": null
},
"_lastParentIsUserSpace": {
"ID": const Uuid().v4(),
"Data": true
},
"__legacyActiveUserRootOnly-ID": const Uuid().v4()
}
},
{
"Type": "FrooxEngine.StaticTexture2D",
"Data": {
"ID": texture2dUid,
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 0
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"URL": {
"ID": const Uuid().v4(),
"Data": "@$imageUri"
},
"FilterMode": {
"ID": const Uuid().v4(),
"Data": "Anisotropic"
},
"AnisotropicLevel": {
"ID": const Uuid().v4(),
"Data": 16
},
"Uncompressed": {
"ID": const Uuid().v4(),
"Data": false
},
"DirectLoad": {
"ID": const Uuid().v4(),
"Data": false
},
"ForceExactVariant": {
"ID": const Uuid().v4(),
"Data": false
},
"PreferredFormat": {
"ID": const Uuid().v4(),
"Data": null
},
"MipMapBias": {
"ID": const Uuid().v4(),
"Data": 0.0
},
"IsNormalMap": {
"ID": const Uuid().v4(),
"Data": false
},
"WrapModeU": {
"ID": const Uuid().v4(),
"Data": "Repeat"
},
"WrapModeV": {
"ID": const Uuid().v4(),
"Data": "Repeat"
},
"PowerOfTwoAlignThreshold": {
"ID": const Uuid().v4(),
"Data": 0.05
},
"CrunchCompressed": {
"ID": const Uuid().v4(),
"Data": true
},
"MaxSize": {
"ID": const Uuid().v4(),
"Data": null
},
"MipMaps": {
"ID": const Uuid().v4(),
"Data": true
},
"MipMapFilter": {
"ID": const Uuid().v4(),
"Data": "Box"
},
"Readable": {
"ID": const Uuid().v4(),
"Data": false
}
}
},
{
"Type": "FrooxEngine.ItemTextureThumbnailSource",
"Data": {
"ID": const Uuid().v4(),
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 0
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"Texture": {
"ID": const Uuid().v4(),
"Data": texture2dUid
},
"Crop": {
"ID": const Uuid().v4(),
"Data": null
}
}
},
{
"Type": "FrooxEngine.SnapPlane",
"Data": {
"ID": const Uuid().v4(),
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 0
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"Normal": {
"ID": const Uuid().v4(),
"Data": [
0.0,
0.0,
1.0
]
},
"SnapParent": {
"ID": const Uuid().v4(),
"Data": null
}
}
},
{
"Type": "FrooxEngine.ReferenceProxy",
"Data": {
"ID": const Uuid().v4(),
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 0
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"Reference": {
"ID": const Uuid().v4(),
"Data": texture2dUid
},
"SpawnInstanceOnTrigger": {
"ID": const Uuid().v4(),
"Data": false
}
}
},
{
"Type": "FrooxEngine.AssetProxy`1[[FrooxEngine.Texture2D, FrooxEngine, Version=2022.1.28.1335, Culture=neutral, PublicKeyToken=null]]",
"Data": {
"ID": const Uuid().v4(),
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 0
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"AssetReference": {
"ID": const Uuid().v4(),
"Data": texture2dUid
}
}
},
{
"Type": "FrooxEngine.UnlitMaterial",
"Data": {
"ID": materialId,
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 0
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"HighPriorityIntegration": {
"ID": const Uuid().v4(),
"Data": false
},
"TintColor": {
"ID": const Uuid().v4(),
"Data": [
1.0,
1.0,
1.0,
1.0
]
},
"Texture": {
"ID": const Uuid().v4(),
"Data": texture2dUid
},
"TextureScale": {
"ID": const Uuid().v4(),
"Data": [
1.0,
1.0
]
},
"TextureOffset": {
"ID": const Uuid().v4(),
"Data": [
0.0,
0.0
]
},
"MaskTexture": {
"ID": const Uuid().v4(),
"Data": null
},
"MaskScale": {
"ID": const Uuid().v4(),
"Data": [
1.0,
1.0
]
},
"MaskOffset": {
"ID": const Uuid().v4(),
"Data": [
0.0,
0.0
]
},
"MaskMode": {
"ID": const Uuid().v4(),
"Data": "MultiplyAlpha"
},
"BlendMode": {
"ID": const Uuid().v4(),
"Data": "Alpha"
},
"AlphaCutoff": {
"ID": const Uuid().v4(),
"Data": 0.5
},
"UseVertexColors": {
"ID": const Uuid().v4(),
"Data": true
},
"Sidedness": {
"ID": const Uuid().v4(),
"Data": "Double"
},
"ZWrite": {
"ID": const Uuid().v4(),
"Data": "Auto"
},
"OffsetTexture": {
"ID": const Uuid().v4(),
"Data": null
},
"OffsetMagnitude": {
"ID": const Uuid().v4(),
"Data": [
0.0,
0.0
]
},
"OffsetTextureScale": {
"ID": const Uuid().v4(),
"Data": [
1.0,
1.0
]
},
"OffsetTextureOffset": {
"ID": const Uuid().v4(),
"Data": [
0.0,
0.0
]
},
"PolarUVmapping": {
"ID": const Uuid().v4(),
"Data": false
},
"PolarPower": {
"ID": const Uuid().v4(),
"Data": 1.0
},
"StereoTextureTransform": {
"ID": const Uuid().v4(),
"Data": false
},
"RightEyeTextureScale": {
"ID": const Uuid().v4(),
"Data": [
1.0,
1.0
]
},
"RightEyeTextureOffset": {
"ID": const Uuid().v4(),
"Data": [
0.0,
0.0
]
},
"DecodeAsNormalMap": {
"ID": const Uuid().v4(),
"Data": false
},
"UseBillboardGeometry": {
"ID": const Uuid().v4(),
"Data": false
},
"UsePerBillboardScale": {
"ID": const Uuid().v4(),
"Data": false
},
"UsePerBillboardRotation": {
"ID": const Uuid().v4(),
"Data": false
},
"UsePerBillboardUV": {
"ID": const Uuid().v4(),
"Data": false
},
"BillboardSize": {
"ID": const Uuid().v4(),
"Data": [
0.005,
0.005
]
},
"OffsetFactor": {
"ID": const Uuid().v4(),
"Data": 0.0
},
"OffsetUnits": {
"ID": const Uuid().v4(),
"Data": 0.0
},
"RenderQueue": {
"ID": const Uuid().v4(),
"Data": -1
},
"_unlit-ID": const Uuid().v4(),
"_unlitBillboard-ID": const Uuid().v4()
}
},
{
"Type": "FrooxEngine.QuadMesh",
"Data": {
"ID": quadMeshUid,
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 0
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"HighPriorityIntegration": {
"ID": const Uuid().v4(),
"Data": false
},
"OverrideBoundingBox": {
"ID": const Uuid().v4(),
"Data": false
},
"OverridenBoundingBox": {
"ID": const Uuid().v4(),
"Data": {
"Min": [
0.0,
0.0,
0.0
],
"Max": [
0.0,
0.0,
0.0
]
}
},
"Rotation": {
"ID": const Uuid().v4(),
"Data": [
0.0,
0.0,
0.0,
1.0
]
},
"Size": {
"ID": quadMeshSizeUid,
"Data": [
1,
height/width
]
},
"UVScale": {
"ID": const Uuid().v4(),
"Data": [
1.0,
1.0
]
},
"ScaleUVWithSize": {
"ID": const Uuid().v4(),
"Data": false
},
"UVOffset": {
"ID": const Uuid().v4(),
"Data": [
0.0,
0.0
]
},
"DualSided": {
"ID": const Uuid().v4(),
"Data": false
},
"UseVertexColors": {
"ID": const Uuid().v4(),
"Data": true
},
"UpperLeftColor": {
"ID": const Uuid().v4(),
"Data": [
1.0,
1.0,
1.0,
1.0
]
},
"LowerLeftColor": {
"ID": const Uuid().v4(),
"Data": [
1.0,
1.0,
1.0,
1.0
]
},
"LowerRightColor": {
"ID": const Uuid().v4(),
"Data": [
1.0,
1.0,
1.0,
1.0
]
},
"UpperRightColor": {
"ID": const Uuid().v4(),
"Data": [
1.0,
1.0,
1.0,
1.0
]
}
}
},
{
"Type": "FrooxEngine.MeshRenderer",
"Data": {
"ID": const Uuid().v4(),
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 0
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"Mesh": {
"ID": const Uuid().v4(),
"Data": quadMeshUid
},
"Materials": {
"ID": const Uuid().v4(),
"Data": [
{
"ID": const Uuid().v4(),
"Data": materialId
}
]
},
"MaterialPropertyBlocks": {
"ID": const Uuid().v4(),
"Data": []
},
"ShadowCastMode": {
"ID": const Uuid().v4(),
"Data": "On"
},
"MotionVectorMode": {
"ID": const Uuid().v4(),
"Data": "Object"
},
"SortingOrder": {
"ID": const Uuid().v4(),
"Data": 0
}
}
},
{
"Type": "FrooxEngine.BoxCollider",
"Data": {
"ID": const Uuid().v4(),
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 1000000
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"Offset": {
"ID": const Uuid().v4(),
"Data": [
0.0,
0.0,
0.0
]
},
"Type": {
"ID": const Uuid().v4(),
"Data": "NoCollision"
},
"Mass": {
"ID": const Uuid().v4(),
"Data": 1.0
},
"CharacterCollider": {
"ID": const Uuid().v4(),
"Data": false
},
"IgnoreRaycasts": {
"ID": const Uuid().v4(),
"Data": false
},
"Size": {
"ID": boxColliderSizeUid,
"Data": [
0.7071067,
0.7071067,
0.0
]
}
}
},
{
"Type": "FrooxEngine.Float2ToFloat3SwizzleDriver",
"Data": {
"ID": const Uuid().v4(),
"persistent-ID": const Uuid().v4(),
"UpdateOrder": {
"ID": const Uuid().v4(),
"Data": 0
},
"Enabled": {
"ID": const Uuid().v4(),
"Data": true
},
"Source": {
"ID": const Uuid().v4(),
"Data": quadMeshSizeUid
},
"Target": {
"ID": const Uuid().v4(),
"Data": boxColliderSizeUid
},
"X": {
"ID": const Uuid().v4(),
"Data": 0
},
"Y": {
"ID": const Uuid().v4(),
"Data": 1
},
"Z": {
"ID": const Uuid().v4(),
"Data": -1
}
}
}
]
},
"Name": {
"ID": const Uuid().v4(),
"Data": "alice"
},
"Tag": {
"ID": const Uuid().v4(),
"Data": null
},
"Active": {
"ID": const Uuid().v4(),
"Data": true
},
"Persistent-ID": const Uuid().v4(),
"Position": {
"ID": const Uuid().v4(),
"Data": [
0.8303015,
1.815294,
0.494639724
]
},
"Rotation": {
"ID": const Uuid().v4(),
"Data": [
1.05315749E-07,
0.0222634021,
-1.08297385E-07,
0.999752164
]
},
"Scale": {
"ID": const Uuid().v4(),
"Data": [
0.9999994,
0.999999464,
0.99999994
]
},
"OrderOffset": {
"ID": const Uuid().v4(),
"Data": 0
},
"ParentReference": const Uuid().v4(),
"Children": []
},
"TypeVersions": {
"FrooxEngine.Grabbable": 2,
"FrooxEngine.QuadMesh": 1,
"FrooxEngine.BoxCollider": 1
}
};
}
}

File diff suppressed because it is too large Load diff

View file

@ -51,6 +51,7 @@ class MessageAsset extends StatelessWidget {
),
);
},
errorWidget: (context, url, error) => const Icon(Icons.image_not_supported, size: 128,),
placeholder: (context, uri) => const CircularProgressIndicator(),
),
const SizedBox(height: 8,),

View file

@ -1,5 +1,6 @@
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:contacts_plus_plus/widgets/messages/message_camera_view.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';
@ -8,8 +9,8 @@ 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 List<(FileType, File)>? initialFiles;
final Function(List<(FileType, File)> files) onChange;
final bool disabled;
@override
@ -17,7 +18,7 @@ class MessageAttachmentList extends StatefulWidget {
}
class _MessageAttachmentListState extends State<MessageAttachmentList> {
final List<File> _loadedFiles = [];
final List<(FileType, File)> _loadedFiles = [];
final ScrollController _scrollController = ScrollController();
bool _showShadow = true;
@ -71,7 +72,7 @@ class _MessageAttachmentListState extends State<MessageAttachmentList> {
title: const Text("Remove attachment"),
content: Text(
"This will remove attachment '${basename(
file.path)}', are you sure?"),
file.$2.path)}', are you sure?"),
actions: [
TextButton(
onPressed: () {
@ -104,8 +105,11 @@ class _MessageAttachmentListState extends State<MessageAttachmentList> {
width: 1
),
),
label: Text(basename(file.path)),
icon: const Icon(Icons.attach_file),
label: Text(basename(file.$2.path)),
icon: switch (file.$1) {
FileType.image => const Icon(Icons.image),
_ => const Icon(Icons.attach_file)
}
),
),
).toList()
@ -115,10 +119,13 @@ class _MessageAttachmentListState extends State<MessageAttachmentList> {
),
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);
final result = await FilePicker.platform.pickFiles(type: FileType.image, allowMultiple: true);
if (result != null) {
setState(() {
_loadedFiles.addAll(
result.files.map((e) => e.path != null ? (FileType.image, File(e.path!)) : null)
.whereNotNull());
});
}
},
icon: const Icon(Icons.add_photo_alternate),
@ -134,6 +141,18 @@ class _MessageAttachmentListState extends State<MessageAttachmentList> {
},
icon: const Icon(Icons.add_a_photo),
),
IconButton(
onPressed: widget.disabled ? null : () async {
final result = await FilePicker.platform.pickFiles(type: FileType.any, allowMultiple: true);
if (result != null) {
setState(() {
_loadedFiles.addAll(
result.files.map((e) => e.path != null ? (FileType.any, File(e.path!)) : null).whereNotNull());
});
}
},
icon: const Icon(Icons.file_present_rounded),
),
],
);
}

View file

@ -1,6 +1,8 @@
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:collection/collection.dart';
import 'package:contacts_plus_plus/apis/record_api.dart';
import 'package:contacts_plus_plus/client_holder.dart';
import 'package:contacts_plus_plus/clients/api_client.dart';
@ -32,7 +34,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
final TextEditingController _messageTextController = TextEditingController();
final ScrollController _sessionListScrollController = ScrollController();
final ScrollController _messageScrollController = ScrollController();
final List<File> _loadedFiles = [];
final List<(FileType, File)> _loadedFiles = [];
bool _hasText = false;
bool _isSending = false;
@ -145,6 +147,27 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
_hasText = false;
}
Future<void> sendRawFileMessage(ApiClient client, MessagingClient mClient, File file, String machineId,
void Function(double progress) progressCallback) async {
final record = await RecordApi.uploadRawFile(
client,
file: file,
machineId: machineId,
progressCallback: progressCallback,
);
final message = Message(
id: record.extractMessageId() ?? Message.generateId(),
recipientId: widget.friend.id,
senderId: client.userId,
type: MessageType.object,
content: jsonEncode(record.toMap()),
sendTime: DateTime.now().toUtc(),
);
mClient.sendMessage(message);
_messageTextController.clear();
_hasText = false;
}
@override
Widget build(BuildContext context) {
@ -319,10 +342,14 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
children: [
TextButton.icon(
onPressed: _isSending ? null : () async {
final result = await FilePicker.platform.pickFiles(type: FileType.image);
if (result != null && result.files.single.path != null) {
final result = await FilePicker.platform.pickFiles(
type: FileType.image, allowMultiple: true);
if (result != null) {
setState(() {
_loadedFiles.add(File(result.files.single.path!));
_loadedFiles.addAll(
result.files.map((e) =>
e.path != null ? (FileType.image, File(e.path!)) : null)
.whereNotNull());
});
}
},
@ -332,16 +359,32 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
TextButton.icon(
onPressed: _isSending ? null : () async {
final picture = await Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const MessageCameraView()));
MaterialPageRoute(builder: (context) => const MessageCameraView())) as File?;
if (picture != null) {
setState(() {
_loadedFiles.add(picture);
_loadedFiles.add((FileType.image, picture));
});
}
},
icon: const Icon(Icons.camera_alt),
label: const Text("Camera"),
),
TextButton.icon(
onPressed: _isSending ? null : () async {
final result = await FilePicker.platform.pickFiles(
type: FileType.any, allowMultiple: true);
if (result != null) {
setState(() {
_loadedFiles.addAll(
result.files.map((e) =>
e.path != null ? (FileType.any, File(e.path!)) : null)
.whereNotNull());
});
}
},
icon: const Icon(Icons.file_present_rounded),
label: const Text("Document"),
),
],
),
(false, []) => null,
@ -349,7 +392,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
MessageAttachmentList(
disabled: _isSending,
initialFiles: _loadedFiles,
onChange: (List<File> loadedFiles) =>
onChange: (List<(FileType, File)> loadedFiles) =>
setState(() {
_loadedFiles.clear();
_loadedFiles.addAll(loadedFiles);
@ -490,7 +533,11 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
splashRadius: 24,
onPressed: _isSending ? null : () async {
final sMsgnr = ScaffoldMessenger.of(context);
final toSend = List.from(_loadedFiles);
final settings = ClientHolder
.of(context)
.settingsClient
.currentSettings;
final toSend = List<(FileType, File)>.from(_loadedFiles);
setState(() {
_isSending = true;
_sendProgress = 0;
@ -501,19 +548,21 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
for (int i = 0; i < toSend.length; i++) {
final totalProgress = i / toSend.length;
final file = toSend[i];
await sendImageMessage(apiClient, mClient, file, ClientHolder
.of(context)
.settingsClient
.currentSettings
.machineId
.valueOrDefault,
if (file.$1 == FileType.image) {
await sendImageMessage(
apiClient, mClient, file.$2, settings.machineId.valueOrDefault,
(progress) =>
setState(() {
_sendProgress = totalProgress + progress * 1 / toSend.length;
}),
);
} else {
await sendRawFileMessage(
apiClient, mClient, file.$2, settings.machineId.valueOrDefault, (progress) =>
setState(() =>
_sendProgress = totalProgress + progress * 1 / toSend.length));
}
}
setState(() {
_sendProgress = null;
});