Skip to content

Commit b54f28f

Browse files
Add ServerTimestamp helpers (#2748)
1 parent 3057e1b commit b54f28f

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

packages/firestore/src/api/user_data_writer.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ export class UserDataWriter<T> {
7878
private convertServerTimestamp(value: ServerTimestampValue): unknown {
7979
switch (this.serverTimestampBehavior) {
8080
case 'previous':
81-
return value.previousValue ? this.convertValue(value.previousValue) : null;
81+
return value.previousValue
82+
? this.convertValue(value.previousValue)
83+
: null;
8284
case 'estimate':
8385
return this.convertTimestamp(value.localWriteTime);
8486
default:
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import * as api from '../protos/firestore_proto_api';
2+
import { Timestamp } from '../api/timestamp';
3+
import { normalizeTimestamp } from './proto_values';
4+
5+
/**
6+
* Represents a locally-applied ServerTimestamp.
7+
*
8+
* Server Timestamps are backed by MapValues that contain an internal field
9+
* `__type__` with a value of `server_timestamp`. The previous value and local
10+
* write time are stored in its `__previous_value__` and `__local_write_time__`
11+
* fields respectively.
12+
*
13+
* Notes:
14+
* - ServerTimestampValue instances are created as the result of applying a
15+
* TransformMutation (see TransformMutation.applyTo()). They can only exist in
16+
* the local view of a document. Therefore they do not need to be parsed or
17+
* serialized.
18+
* - When evaluated locally (e.g. for snapshot.data()), they by default
19+
* evaluate to `null`. This behavior can be configured by passing custom
20+
* FieldValueOptions to value().
21+
* - With respect to other ServerTimestampValues, they sort by their
22+
* localWriteTime.
23+
*/
24+
25+
const SERVER_TIMESTAMP_SENTINEL = 'server_timestamp';
26+
const TYPE_KEY = '__type__';
27+
const PREVIOUS_VALUE_KEY = '__previous_value__';
28+
const LOCAL_WRITE_TIME_KEY = '__local_write_time__';
29+
30+
export function isServerTimestamp(value: api.Value | null): boolean {
31+
const type = (value?.mapValue?.fields || {})[TYPE_KEY]?.stringValue;
32+
return type === SERVER_TIMESTAMP_SENTINEL;
33+
}
34+
35+
/**
36+
* Creates a new ServerTimestamp proto value (using the internal format).
37+
*/
38+
export function serverTimestamp(
39+
localWriteTime: Timestamp,
40+
previousValue: api.Value | null
41+
): api.Value {
42+
const mapValue: api.MapValue = {
43+
fields: {
44+
[TYPE_KEY]: {
45+
stringValue: SERVER_TIMESTAMP_SENTINEL
46+
},
47+
[LOCAL_WRITE_TIME_KEY]: {
48+
timestampValue: {
49+
seconds: localWriteTime.seconds,
50+
nanos: localWriteTime.nanoseconds
51+
}
52+
}
53+
}
54+
};
55+
56+
if (previousValue) {
57+
mapValue.fields![PREVIOUS_VALUE_KEY] = previousValue;
58+
}
59+
60+
return { mapValue };
61+
}
62+
63+
/**
64+
* Returns the value of the field before this ServerTimestamp was set.
65+
*
66+
* Preserving the previous values allows the user to display the last resoled
67+
* value until the backend responds with the timestamp.
68+
*/
69+
export function getPreviousValue(value: api.Value): api.Value | null {
70+
const previousValue = value.mapValue!.fields![PREVIOUS_VALUE_KEY];
71+
72+
if (isServerTimestamp(previousValue)) {
73+
return getPreviousValue(previousValue);
74+
}
75+
return previousValue;
76+
}
77+
78+
/**
79+
* Returns the local time at which this timestamp was first set.
80+
*/
81+
export function getLocalWriteTime(value: api.Value): Timestamp {
82+
const localWriteTime = normalizeTimestamp(
83+
value.mapValue!.fields![LOCAL_WRITE_TIME_KEY].timestampValue!
84+
);
85+
return new Timestamp(localWriteTime.seconds, localWriteTime.nanos);
86+
}

0 commit comments

Comments
 (0)