@liveblocks/react
Advanced tools
Comparing version 0.12.0-beta.1 to 0.12.0-beta.2
@@ -1,2 +0,2 @@ | ||
import { Client, Others, Presence, LiveRecord, LiveList, RecordData, User } from "@liveblocks/client"; | ||
import { Client, Others, Presence, LiveObject, LiveMap, User } from "@liveblocks/client"; | ||
import * as React from "react"; | ||
@@ -128,7 +128,6 @@ declare type LiveblocksProviderProps = { | ||
export declare function useSelf<TPresence extends Presence = Presence>(): User<TPresence> | null; | ||
export declare function useStorage<TRoot extends RecordData>(): [ | ||
root: LiveRecord<TRoot> | null | ||
export declare function useStorage<TRoot extends Record<string, any>>(): [ | ||
root: LiveObject<TRoot> | null | ||
]; | ||
export declare function useRecord<TData>(record: LiveRecord<TData>): TData; | ||
export declare function useList<T extends LiveRecord>(list: LiveList<T>): T[]; | ||
export declare function useMap<TKey extends string, TValue>(key: string): LiveMap<TKey, TValue> | null; | ||
export {}; |
1001
lib/index.js
@@ -20,13 +20,2 @@ Object.defineProperty(exports, '__esModule', { value: true }); | ||
var __assign = function() { | ||
__assign = Object.assign || function __assign(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
function __awaiter(thisArg, _arguments, P, generator) { | ||
@@ -70,2 +59,953 @@ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
function remove(array, item) { | ||
for (let i = 0; i < array.length; i++) { | ||
if (array[i] === item) { | ||
array.splice(i, 1); | ||
break; | ||
} | ||
} | ||
} | ||
var ServerMessageType; | ||
(function (ServerMessageType) { | ||
ServerMessageType[ServerMessageType["UpdatePresence"] = 100] = "UpdatePresence"; | ||
ServerMessageType[ServerMessageType["UserJoined"] = 101] = "UserJoined"; | ||
ServerMessageType[ServerMessageType["UserLeft"] = 102] = "UserLeft"; | ||
ServerMessageType[ServerMessageType["Event"] = 103] = "Event"; | ||
ServerMessageType[ServerMessageType["RoomState"] = 104] = "RoomState"; | ||
ServerMessageType[ServerMessageType["InitialStorageState"] = 200] = "InitialStorageState"; | ||
ServerMessageType[ServerMessageType["UpdateStorage"] = 201] = "UpdateStorage"; | ||
})(ServerMessageType || (ServerMessageType = {})); | ||
var ClientMessageType; | ||
(function (ClientMessageType) { | ||
ClientMessageType[ClientMessageType["UpdatePresence"] = 100] = "UpdatePresence"; | ||
ClientMessageType[ClientMessageType["ClientEvent"] = 103] = "ClientEvent"; | ||
ClientMessageType[ClientMessageType["FetchStorage"] = 200] = "FetchStorage"; | ||
ClientMessageType[ClientMessageType["UpdateStorage"] = 201] = "UpdateStorage"; | ||
})(ClientMessageType || (ClientMessageType = {})); | ||
var CrdtType; | ||
(function (CrdtType) { | ||
CrdtType[CrdtType["Object"] = 0] = "Object"; | ||
CrdtType[CrdtType["List"] = 1] = "List"; | ||
CrdtType[CrdtType["Map"] = 2] = "Map"; | ||
CrdtType[CrdtType["Register"] = 3] = "Register"; | ||
})(CrdtType || (CrdtType = {})); | ||
var OpType; | ||
(function (OpType) { | ||
OpType[OpType["Init"] = 0] = "Init"; | ||
OpType[OpType["SetParentKey"] = 1] = "SetParentKey"; | ||
OpType[OpType["CreateList"] = 2] = "CreateList"; | ||
OpType[OpType["UpdateObject"] = 3] = "UpdateObject"; | ||
OpType[OpType["CreateObject"] = 4] = "CreateObject"; | ||
OpType[OpType["DeleteObject"] = 5] = "DeleteObject"; | ||
OpType[OpType["DeleteObjectKey"] = 6] = "DeleteObjectKey"; | ||
OpType[OpType["CreateMap"] = 7] = "CreateMap"; | ||
OpType[OpType["CreateRegister"] = 8] = "CreateRegister"; | ||
})(OpType || (OpType = {})); | ||
var WebsocketCloseCodes; | ||
(function (WebsocketCloseCodes) { | ||
WebsocketCloseCodes[WebsocketCloseCodes["CLOSE_ABNORMAL"] = 1006] = "CLOSE_ABNORMAL"; | ||
WebsocketCloseCodes[WebsocketCloseCodes["INVALID_MESSAGE_FORMAT"] = 4000] = "INVALID_MESSAGE_FORMAT"; | ||
WebsocketCloseCodes[WebsocketCloseCodes["NOT_ALLOWED"] = 4001] = "NOT_ALLOWED"; | ||
WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_MESSAGES_PER_SECONDS"] = 4002] = "MAX_NUMBER_OF_MESSAGES_PER_SECONDS"; | ||
WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS"] = 4003] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS"; | ||
WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP"] = 4004] = "MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP"; | ||
WebsocketCloseCodes[WebsocketCloseCodes["MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM"] = 4005] = "MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM"; | ||
})(WebsocketCloseCodes || (WebsocketCloseCodes = {})); | ||
const min = 32; | ||
const max = 127; | ||
function makePosition(before, after) { | ||
// No children | ||
if (before == null && after == null) { | ||
return pos([min + 1]); | ||
} | ||
// Insert at the end | ||
if (before != null && after == null) { | ||
return getNextPosition(before); | ||
} | ||
// Insert at the start | ||
if (before == null && after != null) { | ||
return getPreviousPosition(after); | ||
} | ||
return pos(makePositionFromCodes(posCodes(before), posCodes(after))); | ||
} | ||
function getPreviousPosition(after) { | ||
const result = []; | ||
const afterCodes = posCodes(after); | ||
for (let i = 0; i < afterCodes.length; i++) { | ||
const code = afterCodes[i]; | ||
if (code <= min + 1) { | ||
result.push(min); | ||
if (afterCodes.length - 1 === i) { | ||
result.push(max - 1); | ||
break; | ||
} | ||
} | ||
else { | ||
result.push(code - 1); | ||
break; | ||
} | ||
} | ||
return pos(result); | ||
} | ||
function getNextPosition(before) { | ||
const result = []; | ||
const beforeCodes = posCodes(before); | ||
for (let i = 0; i < beforeCodes.length; i++) { | ||
const code = beforeCodes[i]; | ||
if (code === max - 1) { | ||
result.push(code); | ||
if (beforeCodes.length - 1 === i) { | ||
result.push(min + 1); | ||
break; | ||
} | ||
} | ||
else { | ||
result.push(code + 1); | ||
break; | ||
} | ||
} | ||
return pos(result); | ||
} | ||
function makePositionFromCodes(before, after) { | ||
let index = 0; | ||
const result = []; | ||
while (true) { | ||
const beforeDigit = before[index] || min; | ||
const afterDigit = after[index] || max; | ||
if (beforeDigit > afterDigit) { | ||
throw new Error(`Impossible to generate position between ${before} and ${after}`); | ||
} | ||
if (beforeDigit === afterDigit) { | ||
result.push(beforeDigit); | ||
index++; | ||
continue; | ||
} | ||
if (afterDigit - beforeDigit === 1) { | ||
result.push(beforeDigit); | ||
result.push(...makePositionFromCodes(before.slice(index + 1), [])); | ||
break; | ||
} | ||
const mid = beforeDigit + Math.floor((afterDigit - beforeDigit) / 2); | ||
result.push(mid); | ||
break; | ||
} | ||
return result; | ||
} | ||
function posCodes(str) { | ||
const codes = []; | ||
for (let i = 0; i < str.length; i++) { | ||
codes.push(str.charCodeAt(i)); | ||
} | ||
return codes; | ||
} | ||
function pos(codes) { | ||
return String.fromCharCode(...codes); | ||
} | ||
function compare(itemA, itemB) { | ||
const aCodes = posCodes(itemA.position); | ||
const bCodes = posCodes(itemB.position); | ||
const maxLength = Math.max(aCodes.length, bCodes.length); | ||
for (let i = 0; i < maxLength; i++) { | ||
const a = aCodes[i] == null ? min : aCodes[i]; | ||
const b = bCodes[i] == null ? min : bCodes[i]; | ||
if (a === b) { | ||
continue; | ||
} | ||
else { | ||
return a - b; | ||
} | ||
} | ||
throw new Error(`Impossible to compare similar position "${itemA.position}" and "${itemB.position}"`); | ||
} | ||
const INTERNAL = Symbol("liveblocks.internal"); | ||
class LiveObject { | ||
constructor(object = {}) { | ||
this._listeners = []; | ||
this._map = new Map(Object.entries(object)); | ||
} | ||
static deserialize([id, item], parentToChildren, doc) { | ||
if (item.type !== CrdtType.Object) { | ||
throw new Error(`Tried to deserialize a record but item type is "${item.type}"`); | ||
} | ||
const record = new LiveObject(item.data); | ||
record.attach(id, doc, item.parentId, item.parentKey); | ||
const children = parentToChildren.get(id); | ||
if (children == null) { | ||
return record; | ||
} | ||
for (const entry of children) { | ||
const crdt = entry[1]; | ||
if (crdt.parentKey == null) { | ||
throw new Error("Tried to deserialize a crdt but it does not have a parentKey and is not the root"); | ||
} | ||
const child = deserialize(entry, parentToChildren, doc); | ||
record._map.set(crdt.parentKey, child); | ||
} | ||
return record; | ||
} | ||
get [INTERNAL]() { | ||
return { | ||
ctx: this._ctx, | ||
attachChild: this.attachChild.bind(this), | ||
detachChild: this.detachChild.bind(this), | ||
detach: this.detach.bind(this), | ||
attach: this.attach.bind(this), | ||
apply: this.apply.bind(this), | ||
getParentId: this._getParentId.bind(this), | ||
getId: this._getId.bind(this), | ||
}; | ||
} | ||
_getParentId() { | ||
var _a; | ||
return (_a = this._ctx) === null || _a === void 0 ? void 0 : _a.parentId; | ||
} | ||
_getId() { | ||
var _a; | ||
return (_a = this._ctx) === null || _a === void 0 ? void 0 : _a.id; | ||
} | ||
attach(id, doc, parentId, parentKey) { | ||
if (this._ctx) { | ||
throw new Error("LiveObject is already part of the storage"); | ||
} | ||
doc.addItem(id, this); | ||
this._ctx = { | ||
id, | ||
doc: doc, | ||
parentId, | ||
}; | ||
const ops = []; | ||
const createOp = { | ||
id: this._ctx.id, | ||
type: OpType.CreateObject, | ||
parentId, | ||
parentKey, | ||
data: {}, | ||
}; | ||
ops.push(createOp); | ||
for (const [key, value] of this._map) { | ||
if (value instanceof LiveObject) { | ||
ops.push(...value.attach(doc.generateId(), doc, this._ctx.id, key)); | ||
} | ||
else if (value instanceof LiveMap) { | ||
ops.push(...value[INTERNAL].attach(doc.generateId(), doc, this._ctx.id, key)); | ||
} | ||
else if (value instanceof LiveList) { | ||
ops.push(...value[INTERNAL].attach(doc.generateId(), doc, this._ctx.id, key)); | ||
} | ||
else { | ||
createOp.data[key] = value; | ||
} | ||
} | ||
return ops; | ||
} | ||
attachChild(key, child) { | ||
const previousValue = this._map.get(key); | ||
if (previousValue instanceof LiveObject) { | ||
previousValue.detach(); | ||
} | ||
this._map.set(key, child); | ||
this.notify(); | ||
} | ||
detachChild(child) { | ||
for (const [key, value] of this._map) { | ||
if (value === child) { | ||
this._map.delete(key); | ||
} | ||
} | ||
if (child instanceof LiveObject) { | ||
child.detach(); | ||
} | ||
this.notify(); | ||
} | ||
detach() { | ||
if (this._ctx == null) { | ||
return; | ||
} | ||
this._ctx.doc.deleteItem(this._ctx.id); | ||
for (const [, value] of this._map) { | ||
if (value instanceof LiveObject) { | ||
value.detach(); | ||
} | ||
} | ||
} | ||
apply(op) { | ||
if (op.type === OpType.UpdateObject) { | ||
for (const key in op.data) { | ||
const oldValue = this._map.get(key); | ||
if (oldValue instanceof LiveObject) { | ||
oldValue.detach(); | ||
} | ||
const value = op.data[key]; | ||
this._map.set(key, value); | ||
} | ||
this.notify(); | ||
} | ||
else if (op.type === OpType.DeleteObjectKey) { | ||
const key = op.key; | ||
const oldValue = this._map.get(key); | ||
if (oldValue instanceof LiveObject) { | ||
oldValue.detach(); | ||
} | ||
this._map.delete(key); | ||
this.notify(); | ||
} | ||
} | ||
notify() { | ||
for (const listener of this._listeners) { | ||
listener(); | ||
} | ||
} | ||
toObject() { | ||
return Object.fromEntries(this._map); | ||
} | ||
set(key, value) { | ||
// TODO: Find out why typescript complains | ||
this.update({ [key]: value }); | ||
} | ||
get(key) { | ||
return this._map.get(key); | ||
} | ||
delete(key) { | ||
if (this._ctx) { | ||
const item = this._map.get(key); | ||
if (item instanceof LiveObject) { | ||
item.detach(); | ||
} | ||
this._ctx.doc.dispatch([ | ||
{ type: OpType.DeleteObjectKey, id: this._ctx.id, key: key }, | ||
]); | ||
} | ||
this._map.delete(key); | ||
this.notify(); | ||
} | ||
update(overrides) { | ||
if (this._ctx) { | ||
const ops = []; | ||
const updateOperation = { | ||
id: this._ctx.id, | ||
type: OpType.UpdateObject, | ||
data: {}, | ||
}; | ||
ops.push(updateOperation); | ||
for (const key in overrides) { | ||
const oldValue = this._map.get(key); | ||
if (oldValue instanceof LiveObject) { | ||
oldValue.detach(); | ||
} | ||
const value = overrides[key]; | ||
if (isCrdt(value)) { | ||
ops.push(...value[INTERNAL].attach(this._ctx.doc.generateId(), this._ctx.doc, this._ctx.id, key)); | ||
} | ||
else { | ||
updateOperation.data[key] = value; | ||
} | ||
this._map.set(key, value); | ||
} | ||
this._ctx.doc.dispatch(ops); | ||
this.notify(); | ||
} | ||
else { | ||
for (const key in overrides) { | ||
const value = overrides[key]; | ||
this._map.set(key, value); | ||
} | ||
this.notify(); | ||
} | ||
} | ||
subscribe(listener) { | ||
this._listeners.push(listener); | ||
} | ||
unsubscribe(listener) { | ||
remove(this._listeners, listener); | ||
} | ||
} | ||
// TODO: Consider removing default parameter | ||
class LiveMap { | ||
constructor(entries) { | ||
this._listeners = []; | ||
if (entries) { | ||
this._map = new Map(entries.map((entry) => [entry[0], selfOrRegister(entry[1])])); | ||
} | ||
else { | ||
this._map = new Map(); | ||
} | ||
} | ||
static deserialize([id, item], parentToChildren, doc) { | ||
if (item.type !== CrdtType.Map) { | ||
throw new Error(`Tried to deserialize a map but item type is "${item.type}"`); | ||
} | ||
const map = new LiveMap(); | ||
map.attach(id, doc, item.parentId, item.parentKey); | ||
const children = parentToChildren.get(id); | ||
if (children == null) { | ||
return map; | ||
} | ||
for (const entry of children) { | ||
const crdt = entry[1]; | ||
if (crdt.parentKey == null) { | ||
throw new Error("Tried to deserialize a crdt but it does not have a parentKey and is not the root"); | ||
} | ||
const child = deserialize(entry, parentToChildren, doc); | ||
map._map.set(crdt.parentKey, child); | ||
} | ||
return map; | ||
} | ||
get [INTERNAL]() { | ||
return { | ||
ctx: this._ctx, | ||
apply: this.apply.bind(this), | ||
attachChild: this.attachChild.bind(this), | ||
attach: this.attach.bind(this), | ||
detachChild: this.detachChild.bind(this), | ||
getParentId: this._getParentId.bind(this), | ||
getId: this._getId.bind(this), | ||
}; | ||
} | ||
_getParentId() { | ||
var _a; | ||
return (_a = this._ctx) === null || _a === void 0 ? void 0 : _a.parentId; | ||
} | ||
_getId() { | ||
var _a; | ||
return (_a = this._ctx) === null || _a === void 0 ? void 0 : _a.id; | ||
} | ||
apply(op) { } | ||
attach(id, doc, parentId, parentKey) { | ||
if (this._ctx) { | ||
throw new Error("LiveMap is already part of the storage"); | ||
} | ||
doc.addItem(id, this); | ||
this._ctx = { | ||
id, | ||
doc: doc, | ||
parentId, | ||
}; | ||
const ops = []; | ||
const createOp = { | ||
id: this._ctx.id, | ||
type: OpType.CreateMap, | ||
parentId, | ||
parentKey, | ||
}; | ||
ops.push(createOp); | ||
for (const [key, value] of this._map) { | ||
if (isCrdt(value)) { | ||
ops.push(...value[INTERNAL].attach(doc.generateId(), doc, this._ctx.id, key)); | ||
} | ||
} | ||
return ops; | ||
} | ||
attachChild(key, child) { | ||
const previousValue = this._map.get(key); | ||
if (previousValue instanceof LiveObject) { | ||
previousValue[INTERNAL].detach(); | ||
} | ||
this._map.set(key, child); | ||
this.notify(); | ||
} | ||
detachChild(child) { | ||
for (const [key, value] of this._map) { | ||
if (value === child) { | ||
this._map.delete(key); | ||
} | ||
} | ||
if (child instanceof LiveObject) { | ||
child[INTERNAL].detach(); | ||
} | ||
else if (child instanceof LiveRegister) { | ||
child[INTERNAL].detach(); | ||
} | ||
this.notify(); | ||
} | ||
get(key) { | ||
const value = this._map.get(key); | ||
if (value instanceof LiveRegister) { | ||
return value.data; | ||
} | ||
return this._map.get(key); | ||
} | ||
set(key, value) { | ||
if (this._ctx) { | ||
const ops = []; | ||
const oldValue = this._map.get(key); | ||
if (oldValue instanceof LiveObject) { | ||
oldValue[INTERNAL].detach(); | ||
} | ||
if (value instanceof LiveObject) { | ||
ops.push(...value[INTERNAL].attach(this._ctx.doc.generateId(), this._ctx.doc, this._ctx.id, key)); | ||
this._map.set(key, value); | ||
} | ||
else if (value instanceof LiveMap || value instanceof LiveList) { | ||
throw new Error("Adding a map or a list inside map is not yet supported"); | ||
} | ||
else { | ||
const register = new LiveRegister(value); | ||
ops.push(...register[INTERNAL].attach(this._ctx.doc.generateId(), this._ctx.doc, this._ctx.id, key)); | ||
this._map.set(key, register); | ||
} | ||
this._ctx.doc.dispatch(ops); | ||
this.notify(); | ||
} | ||
else { | ||
if (value instanceof LiveObject) { | ||
this._map.set(key, value); | ||
} | ||
else if (value instanceof LiveMap || value instanceof LiveList) { | ||
throw new Error("Adding a map or a list inside map is not yet supported"); | ||
} | ||
else { | ||
const register = new LiveRegister(value); | ||
this._map.set(key, register); | ||
} | ||
this.notify(); | ||
} | ||
} | ||
get size() { | ||
return this._map.size; | ||
} | ||
has(key) { | ||
return this._map.has(key); | ||
} | ||
delete(key) { | ||
if (this._ctx) { | ||
const item = this._map.get(key); | ||
if (item instanceof LiveObject) { | ||
const itemCtx = item[INTERNAL].ctx; | ||
if (itemCtx == null) { | ||
throw new Error("Tried to detach a CRDT that is not yet attached"); | ||
} | ||
item[INTERNAL].detach(); | ||
this._ctx.doc.dispatch([{ type: OpType.DeleteObject, id: itemCtx.id }]); | ||
} | ||
else if (item instanceof LiveRegister) { | ||
const itemCtx = item[INTERNAL].ctx; | ||
if (itemCtx == null) { | ||
throw new Error("Tried to detach a CRDT that is not yet attached"); | ||
} | ||
item[INTERNAL].detach(); | ||
this._ctx.doc.dispatch([{ type: OpType.DeleteObject, id: itemCtx.id }]); | ||
} | ||
} | ||
const isDeleted = this._map.delete(key); | ||
if (isDeleted) { | ||
this.notify(); | ||
} | ||
return isDeleted; | ||
} | ||
entries() { | ||
const innerIterator = this._map.entries(); | ||
return { | ||
[Symbol.iterator]: function () { | ||
return this; | ||
}, | ||
next() { | ||
const iteratorValue = innerIterator.next(); | ||
if (iteratorValue.done) { | ||
return { | ||
done: true, | ||
value: undefined, | ||
}; | ||
} | ||
const entry = iteratorValue.value; | ||
return { | ||
value: [entry[0], selfOrRegisterValue(iteratorValue.value[1])], | ||
}; | ||
}, | ||
}; | ||
} | ||
[Symbol.iterator]() { | ||
return this.entries(); | ||
} | ||
keys() { | ||
return this._map.keys(); | ||
} | ||
values() { | ||
const innerIterator = this._map.values(); | ||
return { | ||
[Symbol.iterator]: function () { | ||
return this; | ||
}, | ||
next() { | ||
const iteratorValue = innerIterator.next(); | ||
if (iteratorValue.done) { | ||
return { | ||
done: true, | ||
value: undefined, | ||
}; | ||
} | ||
return { | ||
value: selfOrRegisterValue(iteratorValue.value), | ||
}; | ||
}, | ||
}; | ||
} | ||
forEach(callback) { | ||
for (const entry of this) { | ||
callback(entry[1], entry[0], this); | ||
} | ||
} | ||
subscribe(listener) { | ||
this._listeners.push(listener); | ||
} | ||
unsubscribe(listener) { | ||
remove(this._listeners, listener); | ||
} | ||
notify() { | ||
for (const listener of this._listeners) { | ||
listener(); | ||
} | ||
} | ||
} | ||
function selfOrRegisterValue(obj) { | ||
if (obj instanceof LiveRegister) { | ||
return obj.data; | ||
} | ||
return obj; | ||
} | ||
function selfOrRegister(obj) { | ||
if (obj instanceof LiveObject) { | ||
return obj; | ||
} | ||
else if (obj instanceof LiveMap || obj instanceof LiveList) { | ||
throw new Error("Nested map and list are not yet supported inside a map"); | ||
} | ||
else { | ||
return new LiveRegister(obj); | ||
} | ||
} | ||
class LiveRegister { | ||
constructor(data) { | ||
this._data = data; | ||
} | ||
get data() { | ||
return this._data; | ||
} | ||
static deserialize([id, item], parentToChildren, doc) { | ||
if (item.type !== CrdtType.Register) { | ||
throw new Error(`Tried to deserialize a map but item type is "${item.type}"`); | ||
} | ||
const register = new LiveRegister(item.data); | ||
register.attach(id, doc, item.parentId, item.parentKey); | ||
return register; | ||
} | ||
get [INTERNAL]() { | ||
return { | ||
ctx: this._ctx, | ||
attach: this.attach.bind(this), | ||
detach: this.detach.bind(this), | ||
attachChild: this.attachChild.bind(this), | ||
detachChild: this.detachChild.bind(this), | ||
getParentId: this._getParentId.bind(this), | ||
getId: this._getId.bind(this), | ||
}; | ||
} | ||
_getParentId() { | ||
var _a; | ||
return (_a = this._ctx) === null || _a === void 0 ? void 0 : _a.parentId; | ||
} | ||
_getId() { | ||
var _a; | ||
return (_a = this._ctx) === null || _a === void 0 ? void 0 : _a.id; | ||
} | ||
detachChild(crdt) { | ||
throw new Error("Cannot detach CRDT on register"); | ||
} | ||
detach() { | ||
if (this._ctx) { | ||
this._ctx.doc.deleteItem(this._ctx.id); | ||
} | ||
} | ||
attach(id, doc, parentId, parentKey) { | ||
if (this._ctx) { | ||
throw new Error("LiveRegister is already part of the storage"); | ||
} | ||
doc.addItem(id, this); | ||
this._ctx = { | ||
id, | ||
doc: doc, | ||
parentId, | ||
}; | ||
const ops = []; | ||
const createOp = { | ||
id, | ||
type: OpType.CreateRegister, | ||
parentId, | ||
parentKey, | ||
data: this._data, | ||
}; | ||
ops.push(createOp); | ||
return ops; | ||
} | ||
attachChild(key, child) { | ||
throw new Error("Cannot attach child to register"); | ||
} | ||
} | ||
class LiveList { | ||
constructor(items = []) { | ||
this._listeners = []; | ||
// TODO: Find a better data structure | ||
this._items = []; | ||
let position = undefined; | ||
for (let i = 0; i < items.length; i++) { | ||
const newPosition = makePosition(position); | ||
const item = selfOrRegister(items[i]); | ||
this._items.push([item, newPosition]); | ||
position = newPosition; | ||
} | ||
} | ||
static deserialize([id, item], parentToChildren, doc) { | ||
const list = new LiveList([]); | ||
list.attach(id, doc, item.parentId, item.parentKey); | ||
const children = parentToChildren.get(id); | ||
if (children == null) { | ||
return list; | ||
} | ||
for (const entry of children) { | ||
const child = LiveObject.deserialize(entry, parentToChildren, doc); | ||
list.attachChild(entry[1].parentKey, child); | ||
} | ||
return list; | ||
} | ||
get [INTERNAL]() { | ||
return { | ||
ctx: this._ctx, | ||
attachChild: this.attachChild.bind(this), | ||
detachChild: this.detachChild.bind(this), | ||
attach: this.attach.bind(this), | ||
detach: this.detach.bind(this), | ||
apply: this.apply.bind(this), | ||
setChildKey: this.setChildKey.bind(this), | ||
getParentId: this._getParentId.bind(this), | ||
getId: this._getId.bind(this), | ||
}; | ||
} | ||
_getParentId() { | ||
var _a; | ||
return (_a = this._ctx) === null || _a === void 0 ? void 0 : _a.parentId; | ||
} | ||
_getId() { | ||
var _a; | ||
return (_a = this._ctx) === null || _a === void 0 ? void 0 : _a.id; | ||
} | ||
attach(id, doc, parentId, parentKey) { | ||
if (this._ctx) { | ||
throw new Error("LiveList is already part of the storage"); | ||
} | ||
doc.addItem(id, this); | ||
this._ctx = { | ||
doc: doc, | ||
id: id, | ||
parentId: parentId, | ||
}; | ||
const ops = []; | ||
const createOp = { | ||
id: this._ctx.id, | ||
type: OpType.CreateList, | ||
parentId, | ||
parentKey, | ||
}; | ||
ops.push(createOp); | ||
for (const [item, position] of this._items) { | ||
ops.push(...item[INTERNAL].attach(doc.generateId(), doc, this._ctx.id, position)); | ||
} | ||
return ops; | ||
} | ||
detach() { | ||
if (this._ctx == null) { | ||
return; | ||
} | ||
this._ctx.doc.deleteItem(this._ctx.id); | ||
} | ||
attachChild(key, child) { | ||
this._items.push([child, key]); | ||
this._items.sort((itemA, itemB) => compare({ position: itemA[1] }, { position: itemB[1] })); | ||
this.notify(); | ||
} | ||
detachChild(child) { | ||
const indexToDelete = this._items.findIndex((item) => item[0] === child); | ||
this._items.splice(indexToDelete); | ||
if (child instanceof LiveObject) { | ||
child[INTERNAL].detach(); | ||
} | ||
this.notify(); | ||
} | ||
setChildKey(key, child) { | ||
const item = this._items.find((item) => item[0] === child); | ||
if (item) { | ||
item[1] = key; | ||
} | ||
this._items.sort((itemA, itemB) => compare({ position: itemA[1] }, { position: itemB[1] })); | ||
this.notify(); | ||
} | ||
apply(op) { } | ||
notify() { | ||
for (const listener of this._listeners) { | ||
listener(); | ||
} | ||
} | ||
push(item) { | ||
const position = this._items.length === 0 | ||
? makePosition() | ||
: makePosition(this._items[this._items.length - 1][1]); | ||
const value = selfOrRegister(item); | ||
this._items.push([value, position]); | ||
this.notify(); | ||
if (this._ctx) { | ||
const ops = value[INTERNAL].attach(this._ctx.doc.generateId(), this._ctx.doc, this._ctx.id, position); | ||
this._ctx.doc.dispatch(ops); | ||
} | ||
} | ||
insert(item, index) { | ||
if (index < 0 || index > this._items.length) { | ||
throw new Error(`Cannot delete list item at index "${index}". index should be between 0 and ${this._items.length}`); | ||
} | ||
let before = this._items[index - 1] ? this._items[index - 1][1] : undefined; | ||
let after = this._items[index] ? this._items[index][1] : undefined; | ||
const position = makePosition(before, after); | ||
const value = selfOrRegister(item); | ||
this._items.push([value, position]); | ||
this._items.sort((itemA, itemB) => compare({ position: itemA[1] }, { position: itemB[1] })); | ||
this.notify(); | ||
if (this._ctx) { | ||
const ops = value[INTERNAL].attach(this._ctx.doc.generateId(), this._ctx.doc, this._ctx.id, position); | ||
this._ctx.doc.dispatch(ops); | ||
} | ||
} | ||
move(index, targetIndex) { | ||
if (targetIndex < 0) { | ||
throw new Error("targetIndex cannot be less than 0"); | ||
} | ||
if (targetIndex >= this._items.length) { | ||
throw new Error("targetIndex cannot be greater or equal than the list length"); | ||
} | ||
if (index < 0) { | ||
throw new Error("index cannot be less than 0"); | ||
} | ||
if (index >= this._items.length) { | ||
throw new Error("index cannot be greater or equal than the list length"); | ||
} | ||
let beforePosition = null; | ||
let afterPosition = null; | ||
if (index < targetIndex) { | ||
afterPosition = | ||
targetIndex === this._items.length - 1 | ||
? undefined | ||
: this._items[targetIndex + 1][1]; | ||
beforePosition = this._items[targetIndex][1]; | ||
} | ||
else { | ||
afterPosition = this._items[targetIndex][1]; | ||
beforePosition = | ||
targetIndex === 0 ? undefined : this._items[targetIndex - 1][1]; | ||
} | ||
const position = makePosition(beforePosition, afterPosition); | ||
const item = this._items[index]; | ||
item[1] = position; | ||
this._items.sort((itemA, itemB) => compare({ position: itemA[1] }, { position: itemB[1] })); | ||
this.notify(); | ||
if (this._ctx) { | ||
const id = item[0][INTERNAL].getId(); | ||
if (id == null) { | ||
throw new Error("Internal error. Cannot set parent key from "); | ||
} | ||
this._ctx.doc.dispatch([ | ||
{ | ||
type: OpType.SetParentKey, | ||
id: id, | ||
parentKey: position, | ||
}, | ||
]); | ||
} | ||
} | ||
delete(index) { | ||
if (index < 0 || index >= this._items.length) { | ||
throw new Error(`Cannot delete list item at index "${index}". index should be between 0 and ${this._items.length - 1}`); | ||
} | ||
const item = this._items[index]; | ||
this._items.splice(index, 1); | ||
if (this._ctx) { | ||
const childRecord = item[0]; | ||
this._ctx.doc.dispatch([ | ||
{ | ||
id: childRecord[INTERNAL].ctx.id, | ||
type: OpType.DeleteObject, | ||
}, | ||
]); | ||
childRecord[INTERNAL].detach(); | ||
} | ||
this.notify(); | ||
} | ||
toArray() { | ||
// TODO: typing | ||
return this._items.map((entry) => selfOrRegisterValue(entry[0])); | ||
} | ||
get(index) { | ||
return selfOrRegisterValue(this._items[index][0]); | ||
} | ||
subscribe(listener) { | ||
this._listeners.push(listener); | ||
} | ||
unsubscribe(listener) { | ||
remove(this._listeners, listener); | ||
} | ||
} | ||
function deserialize(entry, parentToChildren, doc) { | ||
switch (entry[1].type) { | ||
case CrdtType.Object: { | ||
return LiveObject.deserialize(entry, parentToChildren, doc); | ||
} | ||
case CrdtType.List: { | ||
return LiveList.deserialize(entry, parentToChildren, doc); | ||
} | ||
case CrdtType.Map: { | ||
return LiveMap.deserialize(entry, parentToChildren, doc); | ||
} | ||
case CrdtType.Register: { | ||
return LiveRegister.deserialize(entry, parentToChildren, doc); | ||
} | ||
default: { | ||
throw new Error("Unexpected CRDT type"); | ||
} | ||
} | ||
} | ||
function isCrdt(obj) { | ||
return (obj instanceof LiveObject || | ||
obj instanceof LiveMap || | ||
obj instanceof LiveList || | ||
obj instanceof LiveRegister); | ||
} | ||
(undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
(undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
(undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var ClientContext = React.createContext(null); | ||
@@ -315,27 +1255,25 @@ var RoomContext = React.createContext(null); | ||
} | ||
function useRecord(record) { | ||
var _a = React.useState(record.toObject()), data = _a[0], setData = _a[1]; | ||
function useMap(key) { | ||
var _a; | ||
var root = useStorage()[0]; | ||
var _b = React.useState(0), setCount = _b[1]; | ||
React.useEffect(function () { | ||
function onChange() { | ||
setData(__assign({}, record.toObject())); | ||
if (root == null) { | ||
return; | ||
} | ||
record.subscribe(onChange); | ||
return function () { | ||
record.unsubscribe(onChange); | ||
}; | ||
}, [record]); | ||
return data; | ||
} | ||
function useList(list) { | ||
var _a = React.useState(list.toArray()), items = _a[0], setItems = _a[1]; | ||
React.useEffect(function () { | ||
var map = root.get(key); | ||
if (map == null) { | ||
map = new LiveMap(); | ||
root.set(key, map); | ||
} | ||
function onChange() { | ||
setItems(list.toArray()); | ||
setCount(function (x) { return x + 1; }); | ||
} | ||
list.subscribe(onChange); | ||
map.subscribe(onChange); | ||
setCount(function (x) { return x + 1; }); | ||
return function () { | ||
list.unsubscribe(onChange); | ||
return map.unsubscribe(onChange); | ||
}; | ||
}, [list]); | ||
return items; | ||
}, [root]); | ||
return (_a = root === null || root === void 0 ? void 0 : root.get(key)) !== null && _a !== void 0 ? _a : null; | ||
} | ||
@@ -348,6 +1286,5 @@ | ||
exports.useEventListener = useEventListener; | ||
exports.useList = useList; | ||
exports.useMap = useMap; | ||
exports.useMyPresence = useMyPresence; | ||
exports.useOthers = useOthers; | ||
exports.useRecord = useRecord; | ||
exports.useSelf = useSelf; | ||
@@ -354,0 +1291,0 @@ exports.useStorage = useStorage; |
{ | ||
"name": "@liveblocks/react", | ||
"version": "0.12.0-beta.1", | ||
"version": "0.12.0-beta.2", | ||
"description": "", | ||
@@ -26,3 +26,3 @@ "main": "./lib/index.js", | ||
"peerDependencies": { | ||
"@liveblocks/client": "0.12.0-beta.1", | ||
"@liveblocks/client": "0.12.0-beta.2", | ||
"react": "^16.14.0 || ^17" | ||
@@ -29,0 +29,0 @@ }, |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
170932
1407
1