diff --git a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreMessageCodec.java b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreMessageCodec.java index 0e0247915b7c..4375e902d313 100644 --- a/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreMessageCodec.java +++ b/packages/cloud_firestore/cloud_firestore/android/src/main/java/io/flutter/plugins/firebase/firestore/FlutterFirebaseFirestoreMessageCodec.java @@ -23,6 +23,7 @@ import com.google.firebase.firestore.Query; import com.google.firebase.firestore.QuerySnapshot; import com.google.firebase.firestore.SnapshotMetadata; +import com.google.firebase.firestore.VectorValue; import io.flutter.plugin.common.StandardMessageCodec; import java.io.ByteArrayOutputStream; import java.nio.ByteBuffer; @@ -55,6 +56,7 @@ class FlutterFirebaseFirestoreMessageCodec extends StandardMessageCodec { private static final byte DATA_TYPE_FIRESTORE_INSTANCE = (byte) 196; private static final byte DATA_TYPE_FIRESTORE_QUERY = (byte) 197; private static final byte DATA_TYPE_FIRESTORE_SETTINGS = (byte) 198; + private static final byte DATA_TYPE_VECTOR_VALUE = (byte) 199; @Override protected void writeValue(ByteArrayOutputStream stream, Object value) { @@ -70,6 +72,9 @@ protected void writeValue(ByteArrayOutputStream stream, Object value) { writeAlignment(stream, 8); writeDouble(stream, ((GeoPoint) value).getLatitude()); writeDouble(stream, ((GeoPoint) value).getLongitude()); + } else if (value instanceof VectorValue) { + stream.write(DATA_TYPE_VECTOR_VALUE); + writeValue(stream, ((VectorValue) value).toArray()); } else if (value instanceof DocumentReference) { stream.write(DATA_TYPE_DOCUMENT_REFERENCE); FirebaseFirestore firestore = ((DocumentReference) value).getFirestore(); @@ -238,6 +243,13 @@ protected Object readValueOfType(byte type, ByteBuffer buffer) { case DATA_TYPE_GEO_POINT: readAlignment(buffer, 8); return new GeoPoint(buffer.getDouble(), buffer.getDouble()); + case DATA_TYPE_VECTOR_VALUE: + final ArrayList arrayList = (ArrayList) readValue(buffer); + double[] doubleArray = new double[arrayList.size()]; + for (int i = 0; i < arrayList.size(); i++) { + doubleArray[i] = Objects.requireNonNull(arrayList.get(i), "Null value at index " + i); + } + return FieldValue.vector(doubleArray); case DATA_TYPE_DOCUMENT_REFERENCE: FirebaseFirestore firestore = (FirebaseFirestore) readValue(buffer); final String path = (String) readValue(buffer); diff --git a/packages/cloud_firestore/cloud_firestore/example/integration_test/document_reference_e2e.dart b/packages/cloud_firestore/cloud_firestore/example/integration_test/document_reference_e2e.dart index e22eb5deec97..c668ea8f6e38 100644 --- a/packages/cloud_firestore/cloud_firestore/example/integration_test/document_reference_e2e.dart +++ b/packages/cloud_firestore/cloud_firestore/example/integration_test/document_reference_e2e.dart @@ -408,6 +408,7 @@ void runDocumentReferenceTests() { 'null': null, 'timestamp': Timestamp.now(), 'geopoint': const GeoPoint(1, 2), + 'vectorValue': const VectorValue([1, 2, 3]), 'reference': firestore.doc('foo/bar'), 'nan': double.nan, 'infinity': double.infinity, @@ -444,6 +445,11 @@ void runDocumentReferenceTests() { expect(data['geopoint'], isA()); expect((data['geopoint'] as GeoPoint).latitude, equals(1)); expect((data['geopoint'] as GeoPoint).longitude, equals(2)); + expect(data['vectorValue'], isA()); + expect( + (data['vectorValue'] as VectorValue).toArray(), + equals([1, 2, 3]), + ); expect(data['reference'], isA()); expect((data['reference'] as DocumentReference).id, equals('bar')); expect(data['nan'].isNaN, equals(true)); diff --git a/packages/cloud_firestore/cloud_firestore/example/integration_test/e2e_test.dart b/packages/cloud_firestore/cloud_firestore/example/integration_test/e2e_test.dart index 23362554f0b4..44d4742cbe35 100644 --- a/packages/cloud_firestore/cloud_firestore/example/integration_test/e2e_test.dart +++ b/packages/cloud_firestore/cloud_firestore/example/integration_test/e2e_test.dart @@ -22,8 +22,9 @@ import 'settings_e2e.dart'; import 'snapshot_metadata_e2e.dart'; import 'timestamp_e2e.dart'; import 'transaction_e2e.dart'; -import 'write_batch_e2e.dart'; +import 'vector_value_e2e.dart'; import 'web_snapshot_listeners.dart'; +import 'write_batch_e2e.dart'; bool kUseFirestoreEmulator = true; @@ -52,6 +53,7 @@ void main() { runDocumentReferenceTests(); runFieldValueTests(); runGeoPointTests(); + runVectorValueTests(); runQueryTests(); runSnapshotMetadataTests(); runTimestampTests(); diff --git a/packages/cloud_firestore/cloud_firestore/example/integration_test/vector_value_e2e.dart b/packages/cloud_firestore/cloud_firestore/example/integration_test/vector_value_e2e.dart new file mode 100644 index 000000000000..3163a65b36a4 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore/example/integration_test/vector_value_e2e.dart @@ -0,0 +1,172 @@ +// Copyright 2020, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void runVectorValueTests() { + group('$VectorValue', () { + late FirebaseFirestore firestore; + + setUpAll(() async { + firestore = FirebaseFirestore.instance; + }); + + Future>> initializeTest( + String path, + ) async { + String prefixedPath = 'flutter-tests/$path'; + await firestore.doc(prefixedPath).delete(); + return firestore.doc(prefixedPath); + } + + test('sets a $VectorValue & returns one', () async { + DocumentReference> doc = + await initializeTest('vector-value'); + + await doc.set({ + 'foo': const VectorValue([10.0, -10.0]), + }); + + DocumentSnapshot> snapshot = await doc.get(); + + VectorValue vectorValue = snapshot.data()!['foo']; + expect(vectorValue, isA()); + expect(vectorValue.toArray(), equals([10.0, -10.0])); + }); + + test('updates a $VectorValue & returns', () async { + DocumentReference> doc = + await initializeTest('vector-value-update'); + + await doc.set({ + 'foo': const VectorValue([10.0, -10.0]), + }); + + await doc.update({ + 'foo': const VectorValue([-10.0, 10.0]), + }); + + DocumentSnapshot> snapshot = await doc.get(); + + VectorValue vectorValue = snapshot.data()!['foo']; + expect(vectorValue, isA()); + expect(vectorValue.toArray(), equals([-10.0, 10.0])); + }); + + test('handles empty vector', () async { + DocumentReference> doc = + await initializeTest('vector-value-empty'); + + try { + await doc.set({ + 'foo': const VectorValue([]), + }); + fail('Should have thrown an exception'); + } catch (e) { + expect(e, isA()); + expect( + (e as FirebaseException).code.contains('invalid-argument'), + isTrue, + ); + } + }); + + test('handles single dimension vector', () async { + DocumentReference> doc = + await initializeTest('vector-value-single'); + + await doc.set({ + 'foo': const VectorValue([42.0]), + }); + + DocumentSnapshot> snapshot = await doc.get(); + + VectorValue vectorValue = snapshot.data()!['foo']; + expect(vectorValue, isA()); + expect(vectorValue.toArray(), equals([42.0])); + }); + + test('handles maximum dimensions vector', () async { + List maxDimensions = List.filled(2048, 1); + DocumentReference> doc = + await initializeTest('vector-value-max-dimensions'); + + await doc.set({ + 'foo': VectorValue(maxDimensions), + }); + + DocumentSnapshot> snapshot = await doc.get(); + + VectorValue vectorValue = snapshot.data()!['foo']; + expect(vectorValue, isA()); + expect(vectorValue.toArray(), equals(maxDimensions)); + }); + + test('handles maximum dimensions + 1 vector', () async { + List maxPlusOneDimensions = List.filled(2049, 1); + DocumentReference> doc = + await initializeTest('vector-value-max-plus-one'); + + try { + await doc.set({ + 'foo': VectorValue(maxPlusOneDimensions), + }); + + fail('Should have thrown an exception'); + } catch (e) { + expect(e, isA()); + expect( + (e as FirebaseException).code.contains('invalid-argument'), + isTrue, + ); + } + }); + + test('handles very large values in vector', () async { + DocumentReference> doc = + await initializeTest('vector-value-large-values'); + + await doc.set({ + 'foo': const VectorValue([1e10, -1e10]), + }); + + DocumentSnapshot> snapshot = await doc.get(); + + VectorValue vectorValue = snapshot.data()!['foo']; + expect(vectorValue, isA()); + expect(vectorValue.toArray(), equals([1e10, -1e10])); + }); + + test('handles floats in vector', () async { + DocumentReference> doc = + await initializeTest('vector-value-floats'); + + await doc.set({ + 'foo': const VectorValue([3.14, 2.718]), + }); + + DocumentSnapshot> snapshot = await doc.get(); + + VectorValue vectorValue = snapshot.data()!['foo']; + expect(vectorValue, isA()); + expect(vectorValue.toArray(), equals([3.14, 2.718])); + }); + + test('handles negative values in vector', () async { + DocumentReference> doc = + await initializeTest('vector-value-negative'); + + await doc.set({ + 'foo': const VectorValue([-42.0, -100.0]), + }); + + DocumentSnapshot> snapshot = await doc.get(); + + VectorValue vectorValue = snapshot.data()!['foo']; + expect(vectorValue, isA()); + expect(vectorValue.toArray(), equals([-42.0, -100.0])); + }); + }); +} diff --git a/packages/cloud_firestore/cloud_firestore/example/lib/main.dart b/packages/cloud_firestore/cloud_firestore/example/lib/main.dart index 8eab92f7d137..df1d30dfd6dc 100755 --- a/packages/cloud_firestore/cloud_firestore/example/lib/main.dart +++ b/packages/cloud_firestore/cloud_firestore/example/lib/main.dart @@ -232,6 +232,15 @@ class _FilmListState extends State { list.map((e) => e.taskState), ); return; + case 'vectorValue': + const vectorValue = VectorValue([1.0, 2.0, 3.0]); + final vectorValueDoc = await FirebaseFirestore.instance + .collection('firestore-example-app') + .add({'vectorValue': vectorValue}); + + final snapshot = await vectorValueDoc.get(); + print(snapshot.data()); + return; default: return; } @@ -250,6 +259,10 @@ class _FilmListState extends State { value: 'load_bundle', child: Text('Load bundle'), ), + const PopupMenuItem( + value: 'vectorValue', + child: Text('Test Vector Value'), + ), ]; }, ), diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m index e343ab9aafc5..d93b5f2f5480 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreReader.m @@ -37,6 +37,9 @@ - (id)readValueOfType:(UInt8)type { [self readBytes:&longitude length:8]; return [[FIRGeoPoint alloc] initWithLatitude:latitude longitude:longitude]; } + case FirestoreDataTypeVectorValue: { + return [[FIRVectorValue alloc] initWithArray:[self readValue]]; + } case FirestoreDataTypeDocumentReference: { FIRFirestore *firestore = [self readValue]; NSString *documentPath = [self readValue]; diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m index 8e2ea182fa31..f2656d38dbc9 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/FLTFirebaseFirestoreWriter.m @@ -32,6 +32,10 @@ - (void)writeValue:(id)value { [self writeAlignment:8]; [self writeBytes:(UInt8 *)&latitude length:8]; [self writeBytes:(UInt8 *)&longitude length:8]; + } else if ([value isKindOfClass:[FIRVectorValue class]]) { + FIRVectorValue *vector = value; + [self writeByte:FirestoreDataTypeVectorValue]; + [self writeValue:vector.array]; } else if ([value isKindOfClass:[FIRDocumentReference class]]) { FIRDocumentReference *document = value; NSString *documentPath = [document path]; diff --git a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h index 02c7ab21fd9a..b6fed78e7795 100644 --- a/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h +++ b/packages/cloud_firestore/cloud_firestore/ios/cloud_firestore/Sources/cloud_firestore/include/cloud_firestore/Private/FLTFirebaseFirestoreUtils.h @@ -38,6 +38,7 @@ typedef NS_ENUM(UInt8, FirestoreDataType) { FirestoreDataTypeFirestoreInstance = 196, FirestoreDataTypeFirestoreQuery = 197, FirestoreDataTypeFirestoreSettings = 198, + FirestoreDataTypeVectorValue = 199, }; @interface FLTFirebaseFirestoreReaderWriter : FlutterStandardReaderWriter diff --git a/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart b/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart index 84de07f5f5b0..5d5cfcff34d2 100755 --- a/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart +++ b/packages/cloud_firestore/cloud_firestore/lib/cloud_firestore.dart @@ -22,6 +22,7 @@ export 'package:cloud_firestore_platform_interface/cloud_firestore_platform_inte FieldPath, Blob, GeoPoint, + VectorValue, Timestamp, Source, GetOptions, @@ -57,6 +58,7 @@ part 'src/filters.dart'; part 'src/firestore.dart'; part 'src/load_bundle_task.dart'; part 'src/load_bundle_task_snapshot.dart'; +part 'src/persistent_cache_index_manager.dart'; part 'src/query.dart'; part 'src/query_document_snapshot.dart'; part 'src/query_snapshot.dart'; @@ -64,4 +66,3 @@ part 'src/snapshot_metadata.dart'; part 'src/transaction.dart'; part 'src/utils/codec_utility.dart'; part 'src/write_batch.dart'; -part 'src/persistent_cache_index_manager.dart'; diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart index b29b322c63fa..9f9b8e7866e0 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/cloud_firestore_platform_interface.dart @@ -28,16 +28,17 @@ export 'src/platform_interface/platform_interface_firestore.dart'; export 'src/platform_interface/platform_interface_index_definitions.dart'; export 'src/platform_interface/platform_interface_load_bundle_task.dart'; export 'src/platform_interface/platform_interface_load_bundle_task_snapshot.dart'; +export 'src/platform_interface/platform_interface_persistent_cache_index_manager.dart'; export 'src/platform_interface/platform_interface_query.dart'; export 'src/platform_interface/platform_interface_query_snapshot.dart'; export 'src/platform_interface/platform_interface_transaction.dart'; export 'src/platform_interface/platform_interface_write_batch.dart'; -export 'src/platform_interface/platform_interface_persistent_cache_index_manager.dart'; export 'src/platform_interface/utils/load_bundle_task_state.dart'; export 'src/set_options.dart'; export 'src/settings.dart'; export 'src/snapshot_metadata.dart'; export 'src/timestamp.dart'; +export 'src/vector_value.dart'; /// Helper method exposed to determine whether a given [collectionPath] points to /// a valid Firestore collection. diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/utils/firestore_message_codec.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/utils/firestore_message_codec.dart index 616987bee30f..13612b411aec 100644 --- a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/utils/firestore_message_codec.dart +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/method_channel/utils/firestore_message_codec.dart @@ -5,7 +5,7 @@ // TODO(Lyokone): remove once we bump Flutter SDK min version to 3.3 // ignore: unnecessary_import -import 'dart:typed_data'; +import 'dart:core'; import 'package:cloud_firestore_platform_interface/cloud_firestore_platform_interface.dart'; import 'package:cloud_firestore_platform_interface/src/method_channel/method_channel_field_value.dart'; @@ -43,6 +43,7 @@ class FirestoreMessageCodec extends StandardMessageCodec { static const int _kFirestoreInstance = 196; static const int _kFirestoreQuery = 197; static const int _kFirestoreSettings = 198; + static const int _kVectorValue = 199; static const Map _kFieldValueCodes = { @@ -124,6 +125,9 @@ class FirestoreMessageCodec extends StandardMessageCodec { buffer.putUint8(_kInfinity); } else if (value == double.negativeInfinity) { buffer.putUint8(_kNegativeInfinity); + } else if (value is VectorValue) { + buffer.putUint8(_kVectorValue); + writeValue(buffer, value.toArray()); } else { super.writeValue(buffer, value); } @@ -148,6 +152,10 @@ class FirestoreMessageCodec extends StandardMessageCodec { FirebaseFirestorePlatform.instanceFor( app: app, databaseId: databaseId); return firestore.doc(path); + case _kVectorValue: + final List vector = (readValue(buffer)!) as List; + final List doubles = vector.map((e) => e! as double).toList(); + return VectorValue(doubles); case _kBlob: final int length = readSize(buffer); final List bytes = buffer.getUint8List(length); diff --git a/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/vector_value.dart b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/vector_value.dart new file mode 100644 index 000000000000..0e06c400a1a2 --- /dev/null +++ b/packages/cloud_firestore/cloud_firestore_platform_interface/lib/src/vector_value.dart @@ -0,0 +1,28 @@ +// ignore_for_file: require_trailing_commas +// Copyright 2017, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:flutter/foundation.dart'; + +/// Represents a vector value by an array of doubles. +@immutable +class VectorValue { + /// Create [VectorValue] instance. + const VectorValue(this._value); + + final List _value; // ignore: public_member_api_docs + + @override + bool operator ==(Object other) => + other is VectorValue && listEquals(other._value, _value); + + @override + int get hashCode => _value.hashCode; + + @override + String toString() => 'VectorValue(value: $_value)'; + + /// Converts a [VectorValue] to a [List] of [double]. + List toArray() => _value; +} diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore_interop.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore_interop.dart index cee3dd53a502..43b46f49d511 100644 --- a/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore_interop.dart +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/firestore_interop.dart @@ -436,6 +436,22 @@ extension GeoPointJsImplExtension on GeoPointJsImpl { external JSBoolean isEqual(JSObject other); } +@JS('VectorValue') +@staticInterop +external VectorValueJsImpl get VectorValueConstructor; + +@JS('VectorValue') +@staticInterop +class VectorValueJsImpl {} + +extension VectorValueJsImplExtension on VectorValueJsImpl { + external JSArray toArray(); +} + +@JS() +@staticInterop +external VectorValueJsImpl vector(JSArray values); + @JS('Bytes') @staticInterop external BytesJsImpl get BytesConstructor; diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/utils/utils.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/utils/utils.dart index c50b19031cc5..04e9beade181 100644 --- a/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/utils/utils.dart +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/interop/utils/utils.dart @@ -26,6 +26,9 @@ dynamic dartify(dynamic object) { if (jsObject.instanceof(GeoPointConstructor as JSFunction)) { return jsObject; } + if (jsObject.instanceof(VectorValueConstructor as JSFunction)) { + return jsObject; + } if (jsObject.instanceof(TimestampJsConstructor as JSFunction)) { final castedJSObject = jsObject as TimestampJsImpl; return Timestamp( @@ -99,6 +102,12 @@ JSAny? jsify(Object? dartObject) { return dartObject as JSAny; } + // Cannot be done with Dart 3.2 constraints + // ignore: invalid_runtime_check_with_js_interop_types + if (dartObject is VectorValueJsImpl) { + return dartObject as JSAny; + } + if (dartObject is JSAny Function()) { return dartObject.toJS; } diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/decode_utility.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/decode_utility.dart index 02ac06208251..a227cdb710a9 100644 --- a/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/decode_utility.dart +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/decode_utility.dart @@ -41,6 +41,15 @@ class DecodeUtility { value.instanceof(GeoPointConstructor as JSFunction)) { return GeoPoint((value as GeoPointJsImpl).latitude.toDartDouble, (value as GeoPointJsImpl).longitude.toDartDouble); + // Cannot be done with Dart 3.2 constraints + // ignore: invalid_runtime_check_with_js_interop_types + } else if (value is JSObject && + value.instanceof(VectorValueConstructor as JSFunction)) { + return VectorValue((value as VectorValueJsImpl) + .toArray() + .toDart + .map((JSAny? e) => (e! as JSNumber).toDartDouble) + .toList()); } else if (value is DateTime) { return Timestamp.fromDate(value); // Cannot be done with Dart 3.2 constraints diff --git a/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/encode_utility.dart b/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/encode_utility.dart index 42257b0f624d..defa2f1fb702 100644 --- a/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/encode_utility.dart +++ b/packages/cloud_firestore/cloud_firestore_web/lib/src/utils/encode_utility.dart @@ -120,6 +120,8 @@ class EncodeUtility { } else if (value is GeoPoint) { return firestore_interop.GeoPointJsImpl( value.latitude.toJS, value.longitude.toJS); + } else if (value is VectorValue) { + return firestore_interop.vector(value.toArray().jsify()! as JSArray); } else if (value is Blob) { return firestore_interop.BytesJsImpl.fromUint8Array(value.bytes.toJS); } else if (value is DocumentReferenceWeb) {