Esempio n. 1
0
        public static bool OnlyContains <TSection, THeader, TItem, TKey>(
            this Diffing <TSection, THeader, TItem, TKey> .Changeset changeset,
            int insertedSections = 0,
            int deletedSections  = 0,
            int movedSections    = 0,
            int updatedSections  = 0,
            int insertedItems    = 0,
            int deletedItems     = 0,
            int movedItems       = 0,
            int updatedItems     = 0
            )
            where TKey : IEquatable <TKey>
            where TSection : IAnimatableSectionModel <THeader, TItem, TKey>, new()
            where THeader : IDiffable <TKey>
            where TItem : IDiffable <TKey>, IEquatable <TItem>
        {
            if (changeset.InsertedSections.Count != insertedSections)
            {
                return(false);
            }

            if (changeset.DeletedSections.Count != deletedSections)
            {
                return(false);
            }

            if (changeset.MovedSections.Count != movedSections)
            {
                return(false);
            }

            if (changeset.UpdatedSections.Count != updatedSections)
            {
                return(false);
            }

            if (changeset.InsertedItems.Count != insertedItems)
            {
                return(false);
            }

            if (changeset.DeletedItems.Count != deletedItems)
            {
                return(false);
            }

            if (changeset.MovedItems.Count != movedItems)
            {
                return(false);
            }

            if (changeset.UpdatedItems.Count != updatedItems)
            {
                return(false);
            }

            return(true);
        }
        public static IObserver <IEnumerable <TSection> > AnimateSections <TSection, THeader, TModel, TKey>(
            this IReactive <UITableView> reactive,
            BaseTableViewSource <TSection, THeader, TModel> dataSource)
            where TKey : IEquatable <TKey>
            where TSection : IAnimatableSectionModel <THeader, TModel, TKey>, new()
            where TModel : IDiffable <TKey>, IEquatable <TModel>
            where THeader : IDiffable <TKey>
        {
            return(Observer.Create <IEnumerable <TSection> >(finalSections =>
            {
                var initialSections = dataSource.Sections;
                if (initialSections == null || initialSections.Count == 0)
                {
                    dataSource.SetSections(finalSections);
                    reactive.Base.ReloadData();
                    return;
                }

                // if view is not in view hierarchy, performing batch updates will crash the app
                if (reactive.Base.Window == null)
                {
                    dataSource.SetSections(finalSections);
                    reactive.Base.ReloadData();
                    return;
                }

                var stopwatchProvider = Mvx.Resolve <IStopwatchProvider>();
                var stopwatch = stopwatchProvider.Create(MeasuredOperation.Diffing);
                stopwatch.Start();

                var diff = new Diffing <TSection, THeader, TModel, TKey>(initialSections, finalSections);
                var changeset = diff.ComputeDifferences();

                stopwatch.Stop();

                // The changesets have to be applied one after another. Not in one transaction.
                // iOS is picky about the changes which can happen in a single transaction.
                // Don't put BeginUpdates() ... EndUpdates() around the foreach, it has to stay this way,
                // otherwise the app might crash from time to time.

                foreach (var difference in changeset)
                {
                    reactive.Base.BeginUpdates();
                    dataSource.SetSections(difference.FinalSections);
                    reactive.Base.performChangesetUpdates(difference);
                    reactive.Base.EndUpdates();

                    foreach (var section in difference.UpdatedSections)
                    {
                        if (reactive.Base.GetHeaderView(section) is BaseTableHeaderFooterView <THeader> headerView)
                        {
                            headerView.Item = difference.FinalSections[section].Header;
                        }
                    }
                }
            }));
        }
        private static void performChangesetUpdates <TSection, THeader, TModel, TKey>(
            this UITableView tableView,
            Diffing <TSection, THeader, TModel, TKey> .Changeset changes)
            where TKey : IEquatable <TKey>
            where TSection : IAnimatableSectionModel <THeader, TModel, TKey>, new()
            where TModel : IDiffable <TKey>, IEquatable <TModel>
            where THeader : IDiffable <TKey>

        {
            NSIndexSet newIndexSet(List <int> indexes)
            {
                var indexSet = new NSMutableIndexSet();

                foreach (var i in indexes)
                {
                    indexSet.Add((nuint)i);
                }

                return(indexSet as NSIndexSet);
            }

            tableView.DeleteSections(newIndexSet(changes.DeletedSections), UITableViewRowAnimation.Fade);
            // Updated sections doesn't mean reload entire section, somebody needs to update the section view manually
            // otherwise all cells will be reloaded for nothing.
            tableView.InsertSections(newIndexSet(changes.InsertedSections), UITableViewRowAnimation.Fade);

            foreach (var(from, to) in changes.MovedSections)
            {
                tableView.MoveSection(from, to);
            }
            tableView.DeleteRows(
                changes.DeletedItems.Select(item => NSIndexPath.FromRowSection(item.itemIndex, item.sectionIndex)).ToArray(),
                UITableViewRowAnimation.Top
                );

            tableView.InsertRows(
                changes.InsertedItems.Select(item =>
                                             NSIndexPath.FromItemSection(item.itemIndex, item.sectionIndex)).ToArray(),
                UITableViewRowAnimation.Automatic
                );
            tableView.ReloadRows(
                changes.UpdatedItems.Select(item => NSIndexPath.FromRowSection(item.itemIndex, item.sectionIndex))
                .ToArray(),
                // No animation so it doesn't fade showing the cells behind it
                UITableViewRowAnimation.None
                );

            foreach (var(from, to) in changes.MovedItems)
            {
                tableView.MoveRow(
                    NSIndexPath.FromRowSection(from.itemIndex, from.sectionIndex),
                    NSIndexPath.FromRowSection(to.itemIndex, to.sectionIndex)
                    );
            }
        }
Esempio n. 4
0
        public static List <TSection> Apply <TSection, THeader, TElement, TKey>(
            this Diffing <TSection, THeader, TElement, TKey> .Changeset changeset,
            List <TSection> original
            )
            where TKey : IEquatable <TKey>
            where TSection : IAnimatableSectionModel <THeader, TElement, TKey>, new()
            where THeader : IDiffable <TKey>
            where TElement : IDiffable <TKey>, IEquatable <TElement>
        {
            var afterDeletesAndUpdates      = changeset.applyDeletesAndUpdates(original);
            var afterSectionMovesAndInserts = changeset.applySectionMovesAndInserts(afterDeletesAndUpdates);
            var afterItemInsertsAndMoves    = changeset.applyItemInsertsAndMoves(afterSectionMovesAndInserts);

            return(afterItemInsertsAndMoves);
        }
Esempio n. 5
0
        private static List <TSection> applyDeletesAndUpdates <TSection, THeader, TElement, TKey>(
            this Diffing <TSection, THeader, TElement, TKey> .Changeset changeset,
            List <TSection> original
            )
            where TKey : IEquatable <TKey>
            where TSection : IAnimatableSectionModel <THeader, TElement, TKey>, new()
            where THeader : IDiffable <TKey>
            where TElement : IDiffable <TKey>, IEquatable <TElement>
        {
            var resultAfterDeletesAndUpdates =
                SectionModelTypeWrapper <TSection, THeader, TElement> .Wrap(original);

            foreach (var index in changeset.UpdatedItems)
            {
                resultAfterDeletesAndUpdates[index.sectionIndex].Items[index.itemIndex].Updated = true;
            }

            foreach (var index in changeset.DeletedItems)
            {
                resultAfterDeletesAndUpdates[index.sectionIndex].Items[index.itemIndex].Deleted = true;
            }

            foreach (var section in changeset.DeletedSections)
            {
                resultAfterDeletesAndUpdates[section].Deleted = true;
            }

            resultAfterDeletesAndUpdates = resultAfterDeletesAndUpdates.Where(section => !section.Deleted).ToList();

            for (int sectionIndex = 0; sectionIndex < resultAfterDeletesAndUpdates.Count; sectionIndex++)
            {
                var section = resultAfterDeletesAndUpdates[sectionIndex];

                section.Items = section.Items.Where(item => !item.Deleted).ToList();

                for (int itemIndex = 0; itemIndex < section.Items.Count; itemIndex++)
                {
                    var item = section.Items[itemIndex];

                    if (item.Updated)
                    {
                        section.Items[itemIndex] = new ItemModelTypeWrapper <TElement>(changeset.FinalSections[sectionIndex].Items[itemIndex]);
                    }
                }
            }

            return(SectionModelTypeWrapper <TSection, THeader, TElement> .Unwrap(resultAfterDeletesAndUpdates));
        }
Esempio n. 6
0
        private static List <TSection> applyItemInsertsAndMoves <TSection, THeader, TElement, TKey>(
            this Diffing <TSection, THeader, TElement, TKey> .Changeset changeset,
            List <TSection> original
            )
            where TKey : IEquatable <TKey>
            where TSection : IAnimatableSectionModel <THeader, TElement, TKey>, new()
            where THeader : IDiffable <TKey>
            where TElement : IDiffable <TKey>, IEquatable <TElement>
        {
            var resultAfterInsertsAndMoves = original;

            var sourceIndexesThatShouldBeMoved = new HashSet <ItemPath>(changeset.MovedItems.Select(item => item.Item1).ToList());
            var destinationToSourceMapping     = new Dictionary <ItemPath, ItemPath>();

            foreach (var movedItem in changeset.MovedItems)
            {
                destinationToSourceMapping[movedItem.Item2] = movedItem.Item1;
            }

            var insertedItemPaths = new HashSet <ItemPath>(changeset.InsertedItems);

            var insertedPerSection = Enumerable.Repeat(0, original.Count).ToList();
            var movedInSection     = Enumerable.Repeat(0, original.Count).ToList();
            var movedOutSection    = Enumerable.Repeat(0, original.Count).ToList();

            foreach (var insertedItemPath in changeset.InsertedItems)
            {
                insertedPerSection[insertedItemPath.sectionIndex] += 1;
            }

            foreach (var moveItem in changeset.MovedItems)
            {
                movedInSection[moveItem.Item2.sectionIndex]  += 1;
                movedOutSection[moveItem.Item1.sectionIndex] += 1;
            }


            for (int sectionIndex = 0; sectionIndex < resultAfterInsertsAndMoves.Count; sectionIndex++)
            {
                var section = resultAfterInsertsAndMoves[sectionIndex];

                var originalItems = section.Items;

                var nextUntouchedSourceItemIndex = -1;

                bool findNextUntouchedSourceItem()
                {
                    nextUntouchedSourceItemIndex += 1;
                    while (nextUntouchedSourceItemIndex < section.Items.Count &&
                           sourceIndexesThatShouldBeMoved.Contains(new ItemPath(sectionIndex: sectionIndex, itemIndex: nextUntouchedSourceItemIndex)))
                    {
                        nextUntouchedSourceItemIndex += 1;
                    }

                    return(nextUntouchedSourceItemIndex < section.Items.Count);
                }

                var totalCount = section.Items.Count
                                 + insertedPerSection[sectionIndex]
                                 + movedInSection[sectionIndex]
                                 - movedOutSection[sectionIndex];

                var resultItems = new List <TElement>();

                for (int index = 0; index < totalCount; index++)
                {
                    var itemPath = new ItemPath(sectionIndex, index);
                    if (insertedItemPaths.Contains(itemPath))
                    {
                        resultItems.Add(changeset.FinalSections[itemPath.sectionIndex].Items[itemPath.itemIndex]);
                    }
                    else
                    {
                        if (destinationToSourceMapping.ContainsKey(itemPath))
                        {
                            var sourceIndex = destinationToSourceMapping[itemPath];
                            resultItems.Add(original[sourceIndex.sectionIndex].Items[sourceIndex.itemIndex]);
                        }
                        else
                        {
                            if (!findNextUntouchedSourceItem())
                            {
                                throw new Exception("Oooops, wrong commands.");
                            }

                            resultItems.Add(originalItems[nextUntouchedSourceItemIndex]);
                        }
                    }
                }

                var newSection = new TSection();
                newSection.Initialize(section.Header, resultItems);
                resultAfterInsertsAndMoves[sectionIndex] = newSection;
            }

            return(resultAfterInsertsAndMoves);
        }
Esempio n. 7
0
        private static List <TSection> applySectionMovesAndInserts <TSection, THeader, TElement, TKey>(
            this Diffing <TSection, THeader, TElement, TKey> .Changeset changeset,
            List <TSection> original
            )
            where TKey : IEquatable <TKey>
            where TSection : IAnimatableSectionModel <THeader, TElement, TKey>, new()
            where THeader : IDiffable <TKey>
            where TElement : IDiffable <TKey>, IEquatable <TElement>
        {
            var sourceSectionIndexes       = new HashSet <int>(changeset.MovedSections.Select(movement => movement.Item1));
            var destinationToSourceMapping = new Dictionary <int, int>();

            foreach (var movement in changeset.MovedSections)
            {
                destinationToSourceMapping[movement.Item2] = movement.Item1;
            }

            var insertedSectionsIndexes = new HashSet <int>(changeset.InsertedSections);

            var nextUntouchedSourceSectionIndex = -1;

            bool findNextUntouchedSourceSection()
            {
                nextUntouchedSourceSectionIndex += 1;
                while (nextUntouchedSourceSectionIndex < original.Count && sourceSectionIndexes.Contains(nextUntouchedSourceSectionIndex))
                {
                    nextUntouchedSourceSectionIndex += 1;
                }

                return(nextUntouchedSourceSectionIndex < original.Count);
            }

            var totalCount = original.Count + changeset.InsertedSections.Count;

            var results = new List <TSection>();

            for (int index = 0; index < totalCount; index++)
            {
                if (insertedSectionsIndexes.Contains(index))
                {
                    results.Add(changeset.FinalSections[index]);
                }
                else
                {
                    if (destinationToSourceMapping.ContainsKey(index))
                    {
                        var sourceIndex = destinationToSourceMapping[index];
                        results.Add(original[sourceIndex]);
                    }
                    else
                    {
                        if (!findNextUntouchedSourceSection())
                        {
                            throw new Exception("Oooops, wrong commands.");
                        }

                        results.Add(original[nextUntouchedSourceSectionIndex]);
                    }
                }
            }

            return(results);
        }