Skip to content

Commit

Permalink
add all the firestore field value on the web platform
Browse files Browse the repository at this point in the history
  • Loading branch information
mamillastre committed Jan 6, 2025
1 parent 873cfb5 commit 21be503
Show file tree
Hide file tree
Showing 5 changed files with 183 additions and 20 deletions.
17 changes: 17 additions & 0 deletions packages/firestore/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,9 @@ Remove all listeners for this plugin.

#### DocumentData

Document data (for use with {@link @firebase/firestore/lite#(setDoc:1)}) consists of fields mapped to
values.


#### SetDocumentOptions

Expand Down Expand Up @@ -810,6 +813,9 @@ Remove all listeners for this plugin.

#### QueryFilterConstraint

<a href="#queryfilterconstraint">`QueryFilterConstraint`</a> is a helper union type that represents
{@link <a href="#queryfieldfilterconstraint">QueryFieldFilterConstraint</a>} and {@link <a href="#querycompositefilterconstraint">QueryCompositeFilterConstraint</a>}.

<code><a href="#queryfieldfilterconstraint">QueryFieldFilterConstraint</a> | <a href="#querycompositefilterconstraint">QueryCompositeFilterConstraint</a></code>


Expand All @@ -820,11 +826,22 @@ Remove all listeners for this plugin.

#### QueryNonFilterConstraint

<a href="#querynonfilterconstraint">`QueryNonFilterConstraint`</a> is a helper union type that represents
QueryConstraints which are used to narrow or order the set of documents,
but that do not explicitly filter on a document field.
`QueryNonFilterConstraint`s are created by invoking {@link orderBy},
{@link (startAt:1)}, {@link (startAfter:1)}, {@link (endBefore:1)}, {@link (endAt:1)},
{@link limit} or {@link limitToLast} and can then be passed to {@link (query:1)}
to create a new query instance that also contains the `QueryConstraint`.

<code><a href="#queryorderbyconstraint">QueryOrderByConstraint</a> | <a href="#querylimitconstraint">QueryLimitConstraint</a> | <a href="#querystartatconstraint">QueryStartAtConstraint</a> | <a href="#queryendatconstraint">QueryEndAtConstraint</a></code>


#### OrderByDirection

The direction of a {@link orderBy} clause is specified as 'desc' or 'asc'
(descending or ascending).

<code>'desc' | 'asc'</code>


Expand Down
6 changes: 3 additions & 3 deletions packages/firestore/src/client.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Timestamp as OriginalTimestamp } from 'firebase/firestore';

import type { DocumentSnapshot, FirebaseFirestorePlugin } from './definitions';
import type { CustomField } from './internals';
import type { CustomField, CustomTimestamp } from './internals';
import { FIRESTORE_FIELD_TYPE, FIRESTORE_FIELD_VALUE } from './internals';
import { Timestamp } from './timestamp';

Expand Down Expand Up @@ -86,8 +86,8 @@ function parseResultDocumentData(data: any): any {
switch (field[FIRESTORE_FIELD_TYPE]) {
case 'timestamp':
return new Timestamp(
field[FIRESTORE_FIELD_VALUE].seconds,
field[FIRESTORE_FIELD_VALUE].nanoseconds,
(field as CustomTimestamp)[FIRESTORE_FIELD_VALUE].seconds,
(field as CustomTimestamp)[FIRESTORE_FIELD_VALUE].nanoseconds,
);
}
}
Expand Down
105 changes: 102 additions & 3 deletions packages/firestore/src/field-value.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,119 @@
import type { FieldValue as OriginalFieldValue } from 'firebase/firestore';
import { serverTimestamp as originalServerTimestamp } from 'firebase/firestore';
import {
serverTimestamp as originalServerTimestamp,
arrayRemove as originalArrayRemove,
arrayUnion as originalArrayUnion,
deleteField as originalDeleteField,
increment as originalIncrement,
} from 'firebase/firestore';

import type { CustomField } from './internals';
import { FIRESTORE_FIELD_TYPE, FIRESTORE_FIELD_VALUE } from './internals';

type FieldValue = OriginalFieldValue & { toJSON?: () => any };

/**
* Returns a special value that can be used with `setDocument()` or
* `updateDocument()` that tells the server to remove the given elements from any
* array value that already exists on the server. All instances of each element
* specified will be removed from the array. If the field being modified is not
* already an array it will be overwritten with an empty array.
*
* @since 7.0.0
* @param elements - The elements to remove from the array.
* @returns The `FieldValue` sentinel for use in a call to `setDocument()` or
* `updateDocument()`
*/
export function arrayRemove(
...args: Parameters<typeof originalArrayRemove>
): OriginalFieldValue {
return getFieldValue('arrayRemove', originalArrayRemove, args);
}

/**
* Returns a special value that can be used with `setDocument()` or
* `updateDocument()` that tells the server to union the given elements with any array
* value that already exists on the server. Each specified element that doesn't
* already exist in the array will be added to the end. If the field being
* modified is not already an array it will be overwritten with an array
* containing exactly the specified elements.
*
* @since 7.0.0
* @param elements - The elements to union into the array.
* @returns The `FieldValue` sentinel for use in a call to `setDocument()` or
* `updateDocument()`.
*/
export function arrayUnion(
...args: Parameters<typeof originalArrayUnion>
): OriginalFieldValue {
return getFieldValue('arrayUnion', originalArrayUnion, args);
}

/**
* Returns a sentinel for use with `updateDocument()` or
* `setDocument()` with `{merge: true}` to mark a field for deletion.
*
* @since 7.0.0
*/
export function deleteField(
...args: Parameters<typeof originalDeleteField>
): OriginalFieldValue {
return getFieldValue('deleteField', originalDeleteField, args);
}

/**
* Returns a special value that can be used with `setDocument()` or
* `updateDocument()` that tells the server to increment the field's current value by
* the given value.
*
* If either the operand or the current field value uses floating point
* precision, all arithmetic follows IEEE 754 semantics. If both values are
* integers, values outside of JavaScript's safe number range
* (`Number.MIN_SAFE_INTEGER` to `Number.MAX_SAFE_INTEGER`) are also subject to
* precision loss. Furthermore, once processed by the Firestore backend, all
* integer operations are capped between -2^63 and 2^63-1.
*
* If the current field value is not of type `number`, or if the field does not
* yet exist, the transformation sets the field to the given value.
*
* @since 7.0.0
* @param n - The value to increment by.
* @returns The `FieldValue` sentinel for use in a call to `setDocument()` or
* `updateDocument()`
*/
export function increment(
...args: Parameters<typeof originalIncrement>
): OriginalFieldValue {
return getFieldValue('increment', originalIncrement, args);
}

/**
* Returns a sentinel used with `setDocument()` or `updateDocument()` to
* include a server-generated timestamp in the written data.
*
* @since 7.0.0
*/
export function serverTimestamp(
...args: Parameters<typeof originalServerTimestamp>
): OriginalFieldValue {
const fieldValue: FieldValue = originalServerTimestamp(...args);
return getFieldValue('serverTimestamp', originalServerTimestamp, args);
}

/**
* Build the custom FieldVallue
*/
function getFieldValue(
fieldKey: string,
method: (...args: any) => FieldValue,
args: any[],
): OriginalFieldValue {
const fieldValue: FieldValue = method(...args);
fieldValue.toJSON = () =>
({
[FIRESTORE_FIELD_TYPE]: 'fieldvalue',
[FIRESTORE_FIELD_VALUE]: {
method: 'serverTimestamp',
method: fieldKey,
args,
},
} as CustomField);
return fieldValue;
Expand Down
31 changes: 17 additions & 14 deletions packages/firestore/src/internals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,24 @@ export const FIRESTORE_FIELD_TYPE = '_capacitorFirestoreFieldType';
*/
export const FIRESTORE_FIELD_VALUE = '_capacitorFirestoreFieldValue';

export type CustomFieldValue = {
[FIRESTORE_FIELD_TYPE]: 'fieldvalue';
[FIRESTORE_FIELD_VALUE]: {
method: string;
args?: any;
};
};

export type CustomTimestamp = {
[FIRESTORE_FIELD_TYPE]: 'timestamp';
[FIRESTORE_FIELD_VALUE]: {
seconds: number;
nanoseconds: number;
};
};

/**
* A firestore document data custom field
* Used to serialize the special Firestore fields into JSON
*/
export type CustomField =
| {
[FIRESTORE_FIELD_TYPE]: 'fieldvalue';
[FIRESTORE_FIELD_VALUE]: {
method: string;
};
}
| {
[FIRESTORE_FIELD_TYPE]: 'timestamp';
[FIRESTORE_FIELD_VALUE]: {
seconds: number;
nanoseconds: number;
};
};
export type CustomField = CustomFieldValue | CustomTimestamp;
44 changes: 44 additions & 0 deletions packages/firestore/src/timestamp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,22 @@ import { Timestamp as OriginalTimestamp } from 'firebase/firestore';
import type { CustomField } from './internals';
import { FIRESTORE_FIELD_TYPE, FIRESTORE_FIELD_VALUE } from './internals';

/**
* A `Timestamp` represents a point in time independent of any time zone or
* calendar, represented as seconds and fractions of seconds at nanosecond
* resolution in UTC Epoch time.
*
* It is encoded using the Proleptic Gregorian Calendar which extends the
* Gregorian calendar backwards to year one. It is encoded assuming all minutes
* are 60 seconds long, i.e. leap seconds are "smeared" so that no leap second
* table is needed for interpretation. Range is from 0001-01-01T00:00:00Z to
* 9999-12-31T23:59:59.999999999Z.
*
* For examples and further specifications, refer to the
* {@link https://github.com/google/protobuf/blob/master/src/google/protobuf/timestamp.proto | Timestamp definition}.
*
* @since 7.0.0
*/
export class Timestamp extends OriginalTimestamp {
/**
* Creates a new timestamp from a JS SDK firestore timestamp.
Expand All @@ -14,20 +30,48 @@ export class Timestamp extends OriginalTimestamp {
return new Timestamp(timestamp.seconds, timestamp.nanoseconds);
}

/**
* Creates a new timestamp with the current date, with millisecond precision.
*
* @since 7.0.0
* @returns a new timestamp representing the current date.
*/
static now(): Timestamp {
return Timestamp.fromOriginalTimestamp(OriginalTimestamp.now());
}

/**
* Creates a new timestamp from the given date.
*
* @since 7.0.0
* @param date - The date to initialize the `Timestamp` from.
* @returns A new `Timestamp` representing the same point in time as the given
* date.
*/
static fromDate(date: Date): Timestamp {
return Timestamp.fromOriginalTimestamp(OriginalTimestamp.fromDate(date));
}

/**
* Creates a new timestamp from the given number of milliseconds.
*
* @since 7.0.0
* @param milliseconds - Number of milliseconds since Unix epoch
* 1970-01-01T00:00:00Z.
* @returns A new `Timestamp` representing the same point in time as the given
* number of milliseconds.
*/
static fromMillis(milliseconds: number): Timestamp {
return Timestamp.fromOriginalTimestamp(
OriginalTimestamp.fromMillis(milliseconds),
);
}

/**
* Returns a JSON-serializable representation of this `Timestamp`.
*
* @since 7.0.0
*/
public toJSON(): any {
return {
[FIRESTORE_FIELD_TYPE]: 'timestamp',
Expand Down

0 comments on commit 21be503

Please sign in to comment.