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:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
import 'dart:math';
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
import 'dart:ui';
|
||||||
|
import 'package:collection/collection.dart';
|
||||||
import 'package:contacts_plus_plus/auxiliary.dart';
|
import 'package:contacts_plus_plus/auxiliary.dart';
|
||||||
import 'package:contacts_plus_plus/models/message.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:http/http.dart' as http;
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/clients/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/models/records/asset_upload_data.dart';
|
import 'package:contacts_plus_plus/models/records/asset_upload_data.dart';
|
||||||
|
@ -55,17 +60,19 @@ class RecordApi {
|
||||||
ApiClient.checkResponse(response);
|
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(
|
final request = http.MultipartRequest(
|
||||||
"POST",
|
"POST",
|
||||||
ApiClient.buildFullUri("/users/${client.userId}/assets/${asset.hash}/chunks/0"),
|
ApiClient.buildFullUri("/users/${client.userId}/assets/${asset.hash}/chunks/$i"),
|
||||||
)..files.add(http.MultipartFile.fromBytes("file", data, filename: filename, contentType: MediaType.parse("multipart/form-data")))
|
)..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);
|
..headers.addAll(client.authorizationHeader);
|
||||||
final response = await request.send();
|
final response = await request.send();
|
||||||
final bodyBytes = await response.stream.toBytes();
|
final bodyBytes = await response.stream.toBytes();
|
||||||
ApiClient.checkResponse(http.Response.bytes(bodyBytes, response.statusCode));
|
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 {
|
static Future<void> finishUpload(ApiClient client, {required NeosDBAsset asset}) async {
|
||||||
|
@ -73,16 +80,21 @@ class RecordApi {
|
||||||
ApiClient.checkResponse(response);
|
ApiClient.checkResponse(response);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Record> uploadFile(ApiClient client, {required File file, required String machineId}) async {
|
static Future<Record> uploadImage(ApiClient client, {required File image, required String machineId}) async {
|
||||||
final data = await file.readAsBytes();
|
final imageData = await image.readAsBytes();
|
||||||
final asset = NeosDBAsset.fromData(data);
|
final imageImage = await decodeImageFromList(imageData);
|
||||||
final assetUri = "neosdb:///$machineId/${asset.hash}${extension(file.path)}";
|
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 combinedRecordId = RecordId(id: Record.generateId(), ownerId: client.userId, isValid: true);
|
||||||
final filename = basenameWithoutExtension(file.path);
|
final filename = basenameWithoutExtension(image.path);
|
||||||
final record = Record(
|
final record = Record(
|
||||||
id: combinedRecordId.id.toString(),
|
id: combinedRecordId.id.toString(),
|
||||||
combinedRecordId: combinedRecordId,
|
combinedRecordId: combinedRecordId,
|
||||||
assetUri: assetUri,
|
assetUri: objectNeosDbUri,
|
||||||
name: filename,
|
name: filename,
|
||||||
tags: [
|
tags: [
|
||||||
filename,
|
filename,
|
||||||
|
@ -90,12 +102,13 @@ class RecordApi {
|
||||||
"message_id:${Message.generateId()}"
|
"message_id:${Message.generateId()}"
|
||||||
],
|
],
|
||||||
recordType: RecordType.texture,
|
recordType: RecordType.texture,
|
||||||
thumbnailUri: assetUri,
|
thumbnailUri: imageNeosDbUri,
|
||||||
isPublic: false,
|
isPublic: false,
|
||||||
isForPatreons: false,
|
isForPatreons: false,
|
||||||
isListed: false,
|
isListed: false,
|
||||||
neosDBManifest: [
|
neosDBManifest: [
|
||||||
asset,
|
imageAsset,
|
||||||
|
objectAsset,
|
||||||
],
|
],
|
||||||
globalVersion: 0,
|
globalVersion: 0,
|
||||||
localVersion: 1,
|
localVersion: 1,
|
||||||
|
@ -109,7 +122,8 @@ class RecordApi {
|
||||||
path: '',
|
path: '',
|
||||||
description: '',
|
description: '',
|
||||||
manifest: [
|
manifest: [
|
||||||
assetUri
|
imageNeosDbUri,
|
||||||
|
objectNeosDbUri
|
||||||
],
|
],
|
||||||
url: "neosrec:///${client.userId}/${combinedRecordId.id}",
|
url: "neosrec:///${client.userId}/${combinedRecordId.id}",
|
||||||
isValidOwnerId: true,
|
isValidOwnerId: true,
|
||||||
|
@ -128,14 +142,25 @@ class RecordApi {
|
||||||
if (status.state != RecordPreprocessState.success) {
|
if (status.state != RecordPreprocessState.success) {
|
||||||
throw "Record Preprocessing failed: ${status.failReason}";
|
throw "Record Preprocessing failed: ${status.failReason}";
|
||||||
}
|
}
|
||||||
|
AssetUploadData uploadData;
|
||||||
final uploadData = await beginUploadAsset(client, asset: asset);
|
if ((status.resultDiffs.firstWhereOrNull((element) => element.hash == imageAsset.hash)?.isUploaded ?? false) == false) {
|
||||||
|
uploadData = await beginUploadAsset(client, asset: imageAsset);
|
||||||
if (uploadData.uploadState == UploadState.failed) {
|
if (uploadData.uploadState == UploadState.failed) {
|
||||||
throw "Asset upload failed: ${uploadData.uploadState.name}";
|
throw "Asset upload failed: ${uploadData.uploadState.name}";
|
||||||
}
|
}
|
||||||
|
|
||||||
await uploadAsset(client, asset: asset, data: data, filename: filename);
|
await uploadAsset(client, uploadData: uploadData, asset: imageAsset, data: imageData, filename: filename);
|
||||||
await finishUpload(client, asset: asset);
|
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;
|
return record;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -119,6 +119,9 @@ class ApiClient {
|
||||||
|
|
||||||
static void checkResponse(http.Response response) {
|
static void checkResponse(http.Response response) {
|
||||||
final error = "(${response.statusCode}${kDebugMode ? "|${response.body}" : ""})";
|
final error = "(${response.statusCode}${kDebugMode ? "|${response.body}" : ""})";
|
||||||
|
if (response.statusCode >= 300) {
|
||||||
|
FlutterError.reportError(FlutterErrorDetails(exception: error));
|
||||||
|
}
|
||||||
if (response.statusCode == 429) {
|
if (response.statusCode == 429) {
|
||||||
throw "Sorry, you are being rate limited. $error";
|
throw "Sorry, you are being rate limited. $error";
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
|
|
||||||
class AssetDiff {
|
import 'package:contacts_plus_plus/models/records/neos_db_asset.dart';
|
||||||
final String hash;
|
|
||||||
final int bytes;
|
class AssetDiff extends NeosDBAsset{
|
||||||
final Diff state;
|
final Diff state;
|
||||||
final bool isUploaded;
|
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) {
|
factory AssetDiff.fromMap(Map map) {
|
||||||
return AssetDiff(
|
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 'dart:io';
|
||||||
|
|
||||||
import 'package:contacts_plus_plus/apis/record_api.dart';
|
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/client_holder.dart';
|
||||||
import 'package:contacts_plus_plus/clients/api_client.dart';
|
import 'package:contacts_plus_plus/clients/api_client.dart';
|
||||||
import 'package:contacts_plus_plus/clients/messaging_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 {
|
Future<void> sendTextMessage(ScaffoldMessengerState scaffoldMessenger, ApiClient client, MessagingClient mClient, String content) async {
|
||||||
|
if (content.isEmpty) return;
|
||||||
setState(() {
|
setState(() {
|
||||||
_isSending = true;
|
_isSending = true;
|
||||||
});
|
});
|
||||||
|
@ -114,16 +114,11 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
|
||||||
_isSending = true;
|
_isSending = true;
|
||||||
});
|
});
|
||||||
try {
|
try {
|
||||||
var record = await RecordApi.uploadFile(
|
final record = await RecordApi.uploadImage(
|
||||||
client,
|
client,
|
||||||
file: file,
|
image: file,
|
||||||
machineId: machineId,
|
machineId: machineId,
|
||||||
);
|
);
|
||||||
final newUri = Aux.neosDbToHttp(record.assetUri);
|
|
||||||
record = record.copyWith(
|
|
||||||
assetUri: newUri,
|
|
||||||
thumbnailUri: newUri,
|
|
||||||
);
|
|
||||||
|
|
||||||
final message = Message(
|
final message = Message(
|
||||||
id: Message.generateId(),
|
id: Message.generateId(),
|
||||||
|
@ -309,7 +304,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
|
||||||
duration: const Duration(milliseconds: 250),
|
duration: const Duration(milliseconds: 250),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
/*IconButton(
|
IconButton(
|
||||||
onPressed: _hasText ? null : _loadedFile == null ? () async {
|
onPressed: _hasText ? null : _loadedFile == null ? () async {
|
||||||
//final machineId = ClientHolder.of(context).settingsClient.currentSettings.machineId.valueOrDefault;
|
//final machineId = ClientHolder.of(context).settingsClient.currentSettings.machineId.valueOrDefault;
|
||||||
final result = await FilePicker.platform.pickFiles(type: FileType.image);
|
final result = await FilePicker.platform.pickFiles(type: FileType.image);
|
||||||
|
@ -321,7 +316,7 @@ class _MessagesListState extends State<MessagesList> with SingleTickerProviderSt
|
||||||
}
|
}
|
||||||
} : () => setState(() => _loadedFile = null),
|
} : () => setState(() => _loadedFile = null),
|
||||||
icon: _loadedFile == null ? const Icon(Icons.attach_file) : const Icon(Icons.close),
|
icon: _loadedFile == null ? const Icon(Icons.attach_file) : const Icon(Icons.close),
|
||||||
),*/
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(8),
|
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
|
# 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
|
# 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.
|
# 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:
|
environment:
|
||||||
sdk: '>=3.0.0'
|
sdk: '>=3.0.0'
|
||||||
|
|
Loading…
Reference in a new issue