Exemple #1
0
        internal static void CleanupTransactions(IList <Transaction> transactionCleanups, int i)
        {
            if (i < transactionCleanups.Count)
            {
                var transaction  = transactionCleanups[i];
                var doc          = transaction.Doc;
                var store        = doc.Store;
                var ds           = transaction.DeleteSet;
                var mergeStructs = transaction._mergeStructs;
                var actions      = new List <Action>();

                try
                {
                    ds.SortAndMergeDeleteSet();
                    transaction.AfterState = store.GetStateVector();
                    doc._transaction       = null;

                    actions.Add(() =>
                    {
                        doc.InvokeOnBeforeObserverCalls(transaction);
                    });

                    actions.Add(() =>
                    {
                        foreach (var kvp in transaction.Changed)
                        {
                            var itemType = kvp.Key;
                            var subs     = kvp.Value;

                            if (itemType._item == null || !itemType._item.Deleted)
                            {
                                itemType.CallObserver(transaction, subs);
                            }
                        }
                    });

                    actions.Add(() =>
                    {
                        // Deep observe events.
                        foreach (var kvp in transaction.ChangedParentTypes)
                        {
                            var type   = kvp.Key;
                            var events = kvp.Value;

                            // We need to think about the possibility that the user transforms the YDoc in the event.
                            if (type._item == null || !type._item.Deleted)
                            {
                                foreach (var evt in events)
                                {
                                    if (evt.Target._item == null || !evt.Target._item.Deleted)
                                    {
                                        evt.CurrentTarget = type;
                                    }
                                }

                                // Sort events by path length so that top-level events are fired first.
                                var sortedEvents = events.ToList();
                                sortedEvents.Sort((a, b) => a.Path.Count - b.Path.Count);
                                Debug.Assert(sortedEvents.Count > 0);

                                actions.Add(() =>
                                {
                                    type.CallDeepEventHandlerListeners(sortedEvents, transaction);
                                });
                            }
                        }
                    });

                    actions.Add(() =>
                    {
                        doc.InvokeOnAfterTransaction(transaction);
                    });

                    CallAll(actions);
                }
                finally
                {
                    // Replace deleted items with ItemDeleted / GC.
                    // This is where content is actually removed from the Yjs Doc.
                    if (doc.Gc)
                    {
                        ds.TryGcDeleteSet(store, doc.GcFilter);
                    }

                    ds.TryMergeDeleteSet(store);

                    // On all affected store.clients props, try to merge.
                    foreach (var kvp in transaction.AfterState)
                    {
                        var client = kvp.Key;
                        var clock  = kvp.Value;

                        if (!transaction.BeforeState.TryGetValue(client, out int beforeClock))
                        {
                            beforeClock = 0;
                        }

                        if (beforeClock != clock)
                        {
                            var structs        = store.Clients[client];
                            var firstChangePos = Math.Max(StructStore.FindIndexSS(structs, beforeClock), 1);
                            for (int j = structs.Count - 1; j >= firstChangePos; j--)
                            {
                                DeleteSet.TryToMergeWithLeft(structs, j);
                            }
                        }
                    }

                    // Try to merge mergeStructs.
                    // TODO: It makes more sense to transform mergeStructs to a DS, sort it, and merge from right to left
                    //       but at the moment DS does not handle duplicates.
                    for (int j = 0; j < mergeStructs.Count; j++)
                    {
                        var client            = mergeStructs[j].Id.Client;
                        var clock             = mergeStructs[j].Id.Clock;
                        var structs           = store.Clients[client];
                        var replacedStructPos = StructStore.FindIndexSS(structs, clock);

                        if (replacedStructPos + 1 < structs.Count)
                        {
                            DeleteSet.TryToMergeWithLeft(structs, replacedStructPos + 1);
                        }

                        if (replacedStructPos > 0)
                        {
                            DeleteSet.TryToMergeWithLeft(structs, replacedStructPos);
                        }
                    }

                    if (!transaction.Local)
                    {
                        if (!transaction.AfterState.TryGetValue(doc.ClientId, out int afterClock))
                        {
                            afterClock = -1;
                        }

                        if (!transaction.BeforeState.TryGetValue(doc.ClientId, out int beforeClock))
                        {
                            beforeClock = -1;
                        }

                        if (afterClock != beforeClock)
                        {
                            doc.ClientId = YDoc.GenerateNewClientId();
                            // Debug.WriteLine($"{nameof(Transaction)}: Changed the client-id because another client seems to be using it.");
                        }
                    }

                    // @todo: Merge all the transactions into one and provide send the data as a single update message.
                    doc.InvokeOnAfterTransactionCleanup(transaction);

                    doc.InvokeUpdateV2(transaction);

                    foreach (var subDoc in transaction.SubdocsAdded)
                    {
                        doc.Subdocs.Add(subDoc);
                    }

                    foreach (var subDoc in transaction.SubdocsRemoved)
                    {
                        doc.Subdocs.Remove(subDoc);
                    }

                    doc.InvokeSubdocsChanged(transaction.SubdocsLoaded, transaction.SubdocsAdded, transaction.SubdocsRemoved);

                    foreach (var subDoc in transaction.SubdocsRemoved)
                    {
                        subDoc.Destroy();
                    }

                    if (transactionCleanups.Count <= i + 1)
                    {
                        doc._transactionCleanups.Clear();
                        doc.InvokeAfterAllTransactions(transactionCleanups);
                    }
                    else
                    {
                        CleanupTransactions(transactionCleanups, i + 1);
                    }
                }
            }
        }