Files
yjs/src/utils/Transaction.js

111 lines
3.1 KiB
JavaScript
Raw Normal View History

/**
2018-11-25 03:17:00 +01:00
* @module utils
*/
import {
getState,
createID,
writeStructsFromTransaction,
writeDeleteSet,
DeleteSet,
sortAndMergeDeleteSet,
getStates,
2019-04-09 00:31:17 +02:00
AbstractType, AbstractStruct, YEvent, Y // eslint-disable-line
} from '../internals.js'
2019-03-10 23:26:53 +01:00
import * as encoding from 'lib0/encoding.js'
2019-04-09 00:31:17 +02:00
import * as map from 'lib0/map.js'
2019-03-01 23:26:40 +01:00
/**
2018-03-23 04:35:52 +01:00
* A transaction is created for every change on the Yjs model. It is possible
* to bundle changes on the Yjs model in a single transaction to
* minimize the number on messages sent and the number of observer calls.
* If possible the user of this library should bundle as many changes as
* possible. Here is an example to illustrate the advantages of bundling:
*
* @example
* const map = y.define('map', YMap)
* // Log content when change is triggered
2018-11-25 03:17:00 +01:00
* map.observe(() => {
2018-03-23 04:35:52 +01:00
* console.log('change triggered')
* })
* // Each change on the map type triggers a log message:
* map.set('a', 0) // => "change triggered"
* map.set('b', 0) // => "change triggered"
* // When put in a transaction, it will trigger the log after the transaction:
2018-11-25 03:17:00 +01:00
* y.transact(() => {
2018-03-23 04:35:52 +01:00
* map.set('a', 1)
* map.set('b', 1)
* }) // => "change triggered"
*
*/
2018-11-25 03:17:00 +01:00
export class Transaction {
2019-03-26 01:14:15 +01:00
/**
* @param {Y} y
*/
constructor (y) {
2018-03-23 04:35:52 +01:00
/**
2018-11-25 03:17:00 +01:00
* @type {Y} The Yjs instance.
2018-03-23 04:35:52 +01:00
*/
this.y = y
2018-03-23 04:35:52 +01:00
/**
* Describes the set of deleted items by ids
* @type {DeleteSet}
2018-03-23 04:35:52 +01:00
*/
this.deleteSet = new DeleteSet()
2018-03-23 04:35:52 +01:00
/**
* Holds the state before the transaction started.
2018-03-23 04:35:52 +01:00
* @type {Map<Number,Number>}
*/
this.beforeState = getStates(y.store)
/**
* Holds the state after the transaction.
* @type {Map<Number,Number>}
*/
this.afterState = new Map()
2019-03-26 01:14:15 +01:00
/**
* All types that were directly modified (property added or child
* inserted/deleted). New types are not included in this Set.
* Maps from type to parentSubs (`item._parentSub = null` for YArray)
2019-04-03 03:08:10 +02:00
* @type {Map<AbstractType<YEvent>,Set<String|null>>}
2019-03-26 01:14:15 +01:00
*/
this.changed = new Map()
2018-03-23 04:35:52 +01:00
/**
* Stores the events for the types that observe also child elements.
* It is mainly used by `observeDeep`.
2019-04-03 03:08:10 +02:00
* @type {Map<AbstractType<YEvent>,Array<YEvent>>}
2018-03-23 04:35:52 +01:00
*/
2017-11-07 22:44:43 -08:00
this.changedParentTypes = new Map()
2019-03-29 01:02:44 +01:00
/**
* @type {encoding.Encoder|null}
*/
this._updateMessage = null
2019-04-09 00:31:17 +02:00
/**
* @type {Set<AbstractStruct>}
*/
this._replacedItems = new Set()
2019-03-29 01:02:44 +01:00
}
/**
2019-04-09 00:31:17 +02:00
* @type {encoding.Encoder|null}
2019-03-29 01:02:44 +01:00
*/
get updateMessage () {
2019-04-09 00:31:17 +02:00
// only create if content was added in transaction (state or ds changed)
if (this._updateMessage === null && (this.deleteSet.clients.size > 0 || map.any(this.afterState, (clock, client) => this.beforeState.get(client) !== clock))) {
2019-03-29 01:02:44 +01:00
const encoder = encoding.createEncoder()
sortAndMergeDeleteSet(this.deleteSet)
2019-04-05 19:46:18 +02:00
writeStructsFromTransaction(encoder, this)
writeDeleteSet(encoder, this.deleteSet)
2019-03-29 01:02:44 +01:00
this._updateMessage = encoder
}
return this._updateMessage
}
}
2019-03-26 01:14:15 +01:00
/**
* @param {Transaction} transaction
*/
export const nextID = transaction => {
const y = transaction.y
return createID(y.clientID, getState(y.store, y.clientID))
}