Implement chunked file upload and make uploaded files spawnable
This commit is contained in:
parent
362f0cef09
commit
76fcec05de
6 changed files with 818 additions and 38 deletions
|
@ -1,9 +1,14 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
import 'dart:typed_data';
|
||||
import 'dart:ui';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||
import 'package:contacts_plus_plus/models/message.dart';
|
||||
import 'package:contacts_plus_plus/models/records/image_template.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||
import 'package:contacts_plus_plus/models/records/asset_upload_data.dart';
|
||||
|
@ -55,17 +60,19 @@ class RecordApi {
|
|||
ApiClient.checkResponse(response);
|
||||
}
|
||||
|
||||
static Future<dynamic> uploadAsset(ApiClient client, {required String filename, required NeosDBAsset asset, required Uint8List data}) async {
|
||||
static Future<void> uploadAsset(ApiClient client, {required AssetUploadData uploadData, required String filename, required NeosDBAsset asset, required Uint8List data}) async {
|
||||
for (int i = 0; i < uploadData.totalChunks; i++) {
|
||||
final offset = i*uploadData.chunkSize;
|
||||
final end = (i+1)*uploadData.chunkSize;
|
||||
final request = http.MultipartRequest(
|
||||
"POST",
|
||||
ApiClient.buildFullUri("/users/${client.userId}/assets/${asset.hash}/chunks/0"),
|
||||
)..files.add(http.MultipartFile.fromBytes("file", data, filename: filename, contentType: MediaType.parse("multipart/form-data")))
|
||||
ApiClient.buildFullUri("/users/${client.userId}/assets/${asset.hash}/chunks/$i"),
|
||||
)..files.add(http.MultipartFile.fromBytes("file", data.getRange(offset, min(end, data.length)).toList(), filename: filename, contentType: MediaType.parse("multipart/form-data")))
|
||||
..headers.addAll(client.authorizationHeader);
|
||||
final response = await request.send();
|
||||
final bodyBytes = await response.stream.toBytes();
|
||||
ApiClient.checkResponse(http.Response.bytes(bodyBytes, response.statusCode));
|
||||
final body = jsonDecode(bodyBytes.toString());
|
||||
return body;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> finishUpload(ApiClient client, {required NeosDBAsset asset}) async {
|
||||
|
@ -73,16 +80,21 @@ class RecordApi {
|
|||
ApiClient.checkResponse(response);
|
||||
}
|
||||
|
||||
static Future<Record> uploadFile(ApiClient client, {required File file, required String machineId}) async {
|
||||
final data = await file.readAsBytes();
|
||||
final asset = NeosDBAsset.fromData(data);
|
||||
final assetUri = "neosdb:///$machineId/${asset.hash}${extension(file.path)}";
|
||||
static Future<Record> uploadImage(ApiClient client, {required File image, required String machineId}) async {
|
||||
final imageData = await image.readAsBytes();
|
||||
final imageImage = await decodeImageFromList(imageData);
|
||||
final imageAsset = NeosDBAsset.fromData(imageData);
|
||||
final imageNeosDbUri = "neosdb:///${imageAsset.hash}${extension(image.path)}";
|
||||
final objectJson = jsonEncode(ImageTemplate(imageUri: imageNeosDbUri, width: imageImage.width, height: imageImage.height).data);
|
||||
final objectBytes = Uint8List.fromList(utf8.encode(objectJson));
|
||||
final objectAsset = NeosDBAsset.fromData(objectBytes);
|
||||
final objectNeosDbUri = "neosdb:///${objectAsset.hash}.json";
|
||||
final combinedRecordId = RecordId(id: Record.generateId(), ownerId: client.userId, isValid: true);
|
||||
final filename = basenameWithoutExtension(file.path);
|
||||
final filename = basenameWithoutExtension(image.path);
|
||||
final record = Record(
|
||||
id: combinedRecordId.id.toString(),
|
||||
combinedRecordId: combinedRecordId,
|
||||
assetUri: assetUri,
|
||||
assetUri: objectNeosDbUri,
|
||||
name: filename,
|
||||
tags: [
|
||||
filename,
|
||||
|
@ -90,12 +102,13 @@ class RecordApi {
|
|||
"message_id:${Message.generateId()}"
|
||||
],
|
||||
recordType: RecordType.texture,
|
||||
thumbnailUri: assetUri,
|
||||
thumbnailUri: imageNeosDbUri,
|
||||
isPublic: false,
|
||||
isForPatreons: false,
|
||||
isListed: false,
|
||||
neosDBManifest: [
|
||||
asset,
|
||||
imageAsset,
|
||||
objectAsset,
|
||||
],
|
||||
globalVersion: 0,
|
||||
localVersion: 1,
|
||||
|
@ -109,7 +122,8 @@ class RecordApi {
|
|||
path: '',
|
||||
description: '',
|
||||
manifest: [
|
||||
assetUri
|
||||
imageNeosDbUri,
|
||||
objectNeosDbUri
|
||||
],
|
||||
url: "neosrec:///${client.userId}/${combinedRecordId.id}",
|
||||
isValidOwnerId: true,
|
||||
|
@ -128,14 +142,25 @@ class RecordApi {
|
|||
if (status.state != RecordPreprocessState.success) {
|
||||
throw "Record Preprocessing failed: ${status.failReason}";
|
||||
}
|
||||
|
||||
final uploadData = await beginUploadAsset(client, asset: asset);
|
||||
AssetUploadData uploadData;
|
||||
if ((status.resultDiffs.firstWhereOrNull((element) => element.hash == imageAsset.hash)?.isUploaded ?? false) == false) {
|
||||
uploadData = await beginUploadAsset(client, asset: imageAsset);
|
||||
if (uploadData.uploadState == UploadState.failed) {
|
||||
throw "Asset upload failed: ${uploadData.uploadState.name}";
|
||||
}
|
||||
|
||||
await uploadAsset(client, asset: asset, data: data, filename: filename);
|
||||
await finishUpload(client, asset: asset);
|
||||
await uploadAsset(client, uploadData: uploadData, asset: imageAsset, data: imageData, filename: filename);
|
||||
await finishUpload(client, asset: imageAsset);
|
||||
}
|
||||
|
||||
uploadData = await beginUploadAsset(client, asset: objectAsset);
|
||||
if (uploadData.uploadState == UploadState.failed) {
|
||||
throw "Asset upload failed: ${uploadData.uploadState.name}";
|
||||
}
|
||||
|
||||
await uploadAsset(client, uploadData: uploadData, asset: objectAsset, data: objectBytes, filename: filename);
|
||||
await finishUpload(client, asset: objectAsset);
|
||||
|
||||
return record;
|
||||
}
|
||||
}
|
|
@ -119,6 +119,9 @@ class ApiClient {
|
|||
|
||||
static void checkResponse(http.Response response) {
|
||||
final error = "(${response.statusCode}${kDebugMode ? "|${response.body}" : ""})";
|
||||
if (response.statusCode >= 300) {
|
||||
FlutterError.reportError(FlutterErrorDetails(exception: error));
|
||||
}
|
||||
if (response.statusCode == 429) {
|
||||
throw "Sorry, you are being rate limited. $error";
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
|
||||
class AssetDiff {
|
||||
final String hash;
|
||||
final int bytes;
|
||||
import 'package:contacts_plus_plus/models/records/neos_db_asset.dart';
|
||||
|
||||
class AssetDiff extends NeosDBAsset{
|
||||
final Diff state;
|
||||
final bool isUploaded;
|
||||
|
||||
const AssetDiff({required this.hash, required this.bytes, required this.state, required this.isUploaded});
|
||||
const AssetDiff({required hash, required bytes, required this.state, required this.isUploaded}) : super(hash: hash, bytes: bytes);
|
||||
|
||||
factory AssetDiff.fromMap(Map map) {
|
||||
return AssetDiff(
|
||||
|
|
757
lib/models/records/image_template.dart
Normal file
757
lib/models/records/image_template.dart
Normal file
|
@ -0,0 +1,757 @@
|
|||
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
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ import 'dart:convert';
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:contacts_plus_plus/apis/record_api.dart';
|
||||
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||
import 'package:contacts_plus_plus/client_holder.dart';
|
||||
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||
import 'package:contacts_plus_plus/clients/messaging_client.dart';
|
||||
|
@ -80,6 +79,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
|
|||
}
|
||||
|
||||
Future<void> sendTextMessage(ScaffoldMessengerState scaffoldMessenger, ApiClient client, MessagingClient mClient, String content) async {
|
||||
if (content.isEmpty) return;
|
||||
setState(() {
|
||||
_isSending = true;
|
||||
});
|
||||
|
@ -114,16 +114,11 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
|
|||
_isSending = true;
|
||||
});
|
||||
try {
|
||||
var record = await RecordApi.uploadFile(
|
||||
final record = await RecordApi.uploadImage(
|
||||
client,
|
||||
file: file,
|
||||
image: file,
|
||||
machineId: machineId,
|
||||
);
|
||||
final newUri = Aux.neosDbToHttp(record.assetUri);
|
||||
record = record.copyWith(
|
||||
assetUri: newUri,
|
||||
thumbnailUri: newUri,
|
||||
);
|
||||
|
||||
final message = Message(
|
||||
id: Message.generateId(),
|
||||
|
@ -309,7 +304,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
|
|||
duration: const Duration(milliseconds: 250),
|
||||
child: Row(
|
||||
children: [
|
||||
/*IconButton(
|
||||
IconButton(
|
||||
onPressed: _hasText ? null : _loadedFile == null ? () async {
|
||||
//final machineId = ClientHolder.of(context).settingsClient.currentSettings.machineId.valueOrDefault;
|
||||
final result = await FilePicker.platform.pickFiles(type: FileType.image);
|
||||
|
@ -321,7 +316,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
|
|||
}
|
||||
} : () => setState(() => _loadedFile = null),
|
||||
icon: _loadedFile == null ? const Icon(Icons.attach_file) : const Icon(Icons.close),
|
||||
),*/
|
||||
),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8),
|
||||
|
|
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
|||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
# In Windows, build-name is used as the major, minor, and patch parts
|
||||
# of the product and file versions while build-number is used as the build suffix.
|
||||
version: 1.2.3+1
|
||||
version: 1.3.0+1
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0'
|
||||
|
|
Loading…
Reference in a new issue