public static UnifiedBubbleGroup CreateUnified(List <BubbleGroup> groups, BubbleGroup primaryGroup)
        {
            lock (BubbleGroupDatabase.OperationLock)
            {
                var unifiedGroupsToKill = new HashSet <UnifiedBubbleGroup>();
                foreach (var group in groups)
                {
                    if (group.IsUnified)
                    {
                        unifiedGroupsToKill.Add(group.Unified);
                        @group.DeregisterUnified();
                    }
                }
                foreach (var unifiedGroup in unifiedGroupsToKill)
                {
                    BubbleGroupManager.BubbleGroupsRemove(unifiedGroup);
                }
                UnifiedBubbleGroupsDatabase.Remove(unifiedGroupsToKill);

                var unified = CreateUnifiedInternal(groups, primaryGroup);
                UnifiedBubbleGroupsDatabase.Add(unified,
                                                new DisaUnifiedBubbleGroupEntry(unified.ID,
                                                                                unified.Groups.Select(innerGroup => innerGroup.ID)
                                                                                .ToArray(), primaryGroup.ID, primaryGroup.ID));
                BubbleGroupManager.BubbleGroupsAdd(unified);
                return(unified);
            }
        }
Example #2
0
        public static UnifiedBubbleGroup CreateUnified(List <BubbleGroup> groups, BubbleGroup primaryGroup)
        {
            lock (BubbleGroupDatabase.OperationLock)
            {
                var unifiedGroupsToKill = new HashSet <UnifiedBubbleGroup>();
                foreach (var group in groups)
                {
                    if (group.IsUnified)
                    {
                        unifiedGroupsToKill.Add(group.Unified);
                        @group.DeregisterUnified();
                    }
                }
                foreach (var unifiedGroup in unifiedGroupsToKill)
                {
                    BubbleGroupManager.BubbleGroupsRemove(unifiedGroup);
                }
                BubbleGroupIndex.RemoveUnified(unifiedGroupsToKill.Select(x => x.ID).ToArray());

                var unified = CreateUnifiedInternal(groups, primaryGroup);
                BubbleGroupIndex.AddUnified(unified);
                BubbleGroupManager.BubbleGroupsAdd(unified);
                BubbleGroupSettingsManager.SetUnreadIndicatorGuid(unified, unified.LastBubbleSafe().ID, false);
                return(unified);
            }
        }
Example #3
0
        private static BubbleGroup AddNewInternal(VisualBubble newBubble, bool raiseBubbleInserted)
        {
            var group = new BubbleGroup(newBubble, null, false);

            BubbleGroupSettingsManager.SetUnreadIndicatorGuid(group, group.LastBubbleSafe().ID, true);

            if (ServiceManager.IsRunning(@group.Service))
            {
                newBubble.Service.NewBubbleGroupCreated(@group).ContinueWith(x =>
                {
                    // force the UI to refetch the photo
                    @group.IsPhotoSetFromService = false;
                    BubbleManager.SendSubscribe(@group, true);
                    BubbleGroupUpdater.Update(@group);
                });
            }

            BubbleGroupManager.BubbleGroupsAdd(@group);

            BubbleGroupDatabase.AddBubble(@group, newBubble);

            if (raiseBubbleInserted)
            {
                try
                {
                    BubbleGroupEvents.RaiseBubbleInserted(newBubble, @group);
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint(
                        "Error in notifying the interface that the bubble group has been updated (" +
                        newBubble.Service.Information.ServiceName + "): " + ex.Message);
                }
            }

            BubbleGroupUpdater.Update(@group);

            return(@group);
        }
Example #4
0
        internal static BubbleGroup Group(VisualBubble vb, bool resend = false, bool insertAtBottom = false)
        {
            lock (BubbleGroupDatabase.OperationLock)
            {
                Utils.DebugPrint("Grouping an " + vb.Direction + " bubble on service " + vb.Service.Information.ServiceName);

                var theGroup =
                    BubbleGroupManager.FindWithAddress(vb.Service, vb.Address);

                BubbleGroupFactory.LoadFullyIfNeeded(theGroup);

                var duplicate = false;
                var newGroup  = false;
                if (theGroup == null)
                {
                    Utils.DebugPrint(vb.Service.Information.ServiceName + " unable to find suitable group. Creating a new one.");

                    theGroup = new BubbleGroup(vb, null, false);

                    newGroup = true;

                    Utils.DebugPrint("GUID of new group: " + theGroup.ID);

                    vb.Service.NewBubbleGroupCreated(theGroup).ContinueWith(x =>
                    {
                        // force the UI to refetch the photo
                        theGroup.IsPhotoSetFromService = false;
                        SendSubscribe(theGroup, true);
                        BubbleGroupUpdater.Update(theGroup);
                    });

                    BubbleGroupManager.BubbleGroupsAdd(theGroup);
                }
                else
                {
                    if (resend)
                    {
                        if (vb.Status == Bubble.BubbleStatus.Failed)
                        {
                            UpdateStatus(vb, Bubble.BubbleStatus.Waiting, theGroup);
                        }
                        return(theGroup);
                    }

                    var visualBubbleServiceId = vb.Service as IVisualBubbleServiceId;
                    if (visualBubbleServiceId != null &&
                        visualBubbleServiceId.DisctinctIncomingVisualBubbleIdServices())
                    {
                        if (vb.IdService != null)
                        {
                            duplicate = theGroup.Bubbles.FirstOrDefault(x => x.GetType() == vb.GetType() && x.IdService == vb.IdService) != null;
                        }
                        if (!duplicate && vb.IdService2 != null)
                        {
                            duplicate = theGroup.Bubbles.FirstOrDefault(x => x.GetType() == vb.GetType() && x.IdService2 == vb.IdService2) != null;
                        }
                    }

                    if (!duplicate)
                    {
                        Utils.DebugPrint(vb.Service.Information.ServiceName + " found a group. Adding.");

                        if (insertAtBottom)
                        {
                            var lastBubble = theGroup.LastBubbleSafe();
                            if (lastBubble.Time > vb.Time)
                            {
                                vb.Time = lastBubble.Time;
                            }
                        }

                        theGroup.InsertByTime(vb);
                    }
                    else
                    {
                        Utils.DebugPrint("Yuck. It's a duplicate bubble. No need to readd: " + vb.IdService + ", " + vb.IdService2);
                    }
                }

                if (!duplicate)
                {
                    Utils.DebugPrint("Inserting bubble into database group!");

                    try
                    {
                        if (newGroup)
                        {
                            BubbleGroupDatabase.AddBubble(theGroup, vb);
                        }
                        else
                        {
                            BubbleGroupDatabase.InsertBubbleByTime(theGroup, vb);
                        }
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint("Bubble failed to be inserting/added into the group " + theGroup.ID + ": " + ex);
                    }

                    try
                    {
                        BubbleGroupEvents.RaiseBubbleInserted(vb, theGroup);
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint(
                            "Error in notifying the interface that the bubble group has been updated (" +
                            vb.Service.Information.ServiceName + "): " + ex.Message);
                    }
                }

                return(theGroup);
            }
        }
Example #5
0
        internal static BubbleGroup Group(VisualBubble vb, bool resend = false, bool insertAtBottom = false)
        {
            lock (BubbleGroupDatabase.OperationLock)
            {
                Utils.DebugPrint("Grouping an " + vb.Direction + " bubble on service " + vb.Service.Information.ServiceName);

                var theGroup =
                    BubbleGroupManager.FindWithAddress(vb.Service, vb.Address);

                BubbleGroupFactory.LoadFullyIfNeeded(theGroup);

                var duplicate = false;
                var newGroup  = false;
                if (theGroup == null)
                {
                    Utils.DebugPrint(vb.Service.Information.ServiceName + " unable to find suitable group. Creating a new one.");

                    theGroup = new BubbleGroup(vb, null, false);

                    newGroup = true;

                    Utils.DebugPrint("GUID of new group: " + theGroup.ID);

                    BubbleGroupSettingsManager.SetUnreadIndicatorGuid(theGroup, theGroup.LastBubbleSafe().ID, true);

                    vb.Service.NewBubbleGroupCreated(theGroup).ContinueWith(x =>
                    {
                        // force the UI to refetch the photo
                        theGroup.IsPhotoSetFromService = false;
                        SendSubscribe(theGroup, true);
                        BubbleGroupUpdater.Update(theGroup);
                    });

                    BubbleGroupManager.BubbleGroupsAdd(theGroup);
                }
                else
                {
                    if (resend)
                    {
                        if (vb.Status == Bubble.BubbleStatus.Failed)
                        {
                            UpdateStatus(vb, Bubble.BubbleStatus.Waiting, theGroup);
                        }
                        return(theGroup);
                    }

                    var visualBubbleServiceId = vb.Service as IVisualBubbleServiceId;
                    if (visualBubbleServiceId != null &&
                        visualBubbleServiceId.DisctinctIncomingVisualBubbleIdServices())
                    {
                        if (vb.IdService != null)
                        {
                            duplicate = theGroup.Bubbles.FirstOrDefault(x => x.GetType() == vb.GetType() && x.IdService == vb.IdService) != null;
                        }
                        if (!duplicate && vb.IdService2 != null)
                        {
                            duplicate = theGroup.Bubbles.FirstOrDefault(x => x.GetType() == vb.GetType() && x.IdService2 == vb.IdService2) != null;
                        }
                    }

                    if (!duplicate)
                    {
                        Utils.DebugPrint(vb.Service.Information.ServiceName + " found a group. Adding.");

                        if (insertAtBottom)
                        {
                            var lastBubble = theGroup.LastBubbleSafe();
                            if (lastBubble.Time > vb.Time)
                            {
                                vb.Time = lastBubble.Time;
                            }
                        }

                        theGroup.InsertByTime(vb);
                    }
                    else
                    {
                        Utils.DebugPrint("Yuck. It's a duplicate bubble. No need to readd: " + vb.IdService + ", " + vb.IdService2);
                    }
                }

                try
                {
                    if (theGroup.IsParty && !string.IsNullOrWhiteSpace(vb.ParticipantAddressNickname))
                    {
                        var participantAddressNicknamesArray = theGroup.ParticipantNicknames;
                        if (participantAddressNicknamesArray == null)
                        {
                            participantAddressNicknamesArray = new DisaParticipantNickname[0];
                        }
                        var participantAddressNicknames = participantAddressNicknamesArray.ToList();
                        var changed = false;
                        var adding  = true;
                        foreach (var participantAddressNickname in participantAddressNicknames)
                        {
                            if (theGroup.Service.BubbleGroupComparer(participantAddressNickname.Address, vb.ParticipantAddress))
                            {
                                if (participantAddressNickname.Nickname != vb.ParticipantAddressNickname)
                                {
                                    participantAddressNickname.Nickname = vb.ParticipantAddressNickname;
                                    changed = true;
                                }
                                adding = false;
                                break;
                            }
                        }
                        if (adding)
                        {
                            participantAddressNicknames.Add(new DisaParticipantNickname
                            {
                                Address  = vb.ParticipantAddress,
                                Nickname = vb.ParticipantAddressNickname,
                            });
                        }
                        if (changed || adding)
                        {
                            theGroup.ParticipantNicknames = participantAddressNicknames.ToArray();
                        }
                    }
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint("Failed to insert/update participant nickname into cache: " + ex);
                }

                if (!duplicate)
                {
                    Utils.DebugPrint("Inserting bubble into database group!");

                    try
                    {
                        if (newGroup)
                        {
                            BubbleGroupDatabase.AddBubble(theGroup, vb);
                        }
                        else
                        {
                            BubbleGroupDatabase.InsertBubbleByTime(theGroup, vb);
                        }
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint("Bubble failed to be inserting/added into the group " + theGroup.ID + ": " + ex);
                    }

                    try
                    {
                        BubbleGroupEvents.RaiseBubbleInserted(vb, theGroup);
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint(
                            "Error in notifying the interface that the bubble group has been updated (" +
                            vb.Service.Information.ServiceName + "): " + ex.Message);
                    }
                }

                return(theGroup);
            }
        }
Example #6
0
        private static bool LoadInternal(SqlDatabase <Entry> db)
        {
            var entries = GetAllEntries(db);

            if (entries == null)
            {
                return(false);
            }

            var bubbleGroupsActualCount = Directory.GetFiles(BubbleGroupDatabase.GetBaseLocation(), "*.*", SearchOption.AllDirectories).Length;
            var entriesCount            = entries.Count(x => !x.Unified);

            if (!(bubbleGroupsActualCount > entriesCount - 5 &&
                  bubbleGroupsActualCount < entriesCount + 5))
            {
                Utils.DebugPrint("Actual count does not match index count (+-5 tolerance). We'll have to re-generate index.");
                return(false);
            }

            foreach (var entry in entries)
            {
                if (entry.Unified)
                {
                    continue;
                }

                var service = ServiceManager.GetByName(entry.Service);
                if (service != null)
                {
                    var mostRecentVisualBubble = DeserializeBubble(entry.LastBubble);
                    mostRecentVisualBubble.Service = service;
                    mostRecentVisualBubble.ID      = entry.LastBubbleGuid;

                    var bubbleGroup = new BubbleGroup(mostRecentVisualBubble, entry.Guid, true);

                    BubbleGroupManager.BubbleGroupsAdd(bubbleGroup, true);
                }
                else
                {
                    Utils.DebugPrint("Could not obtain a valid service object from " + entry.Id + " (" + entry.Service + ")");
                }
            }

            foreach (var entry in entries)
            {
                if (!entry.Unified)
                {
                    continue;
                }

                var innerGroups = new List <BubbleGroup>();

                var innerGroupsIds = entry.UnifiedBubbleGroupsGuids.Split(',');
                if (innerGroupsIds != null)
                {
                    foreach (var innerGroupId in innerGroupsIds)
                    {
                        var innerGroup = BubbleGroupManager.Find(innerGroupId);
                        if (innerGroup == null)
                        {
                            Utils.DebugPrint("Unified group, inner group " + innerGroupId + " could not be related.");
                        }
                        else
                        {
                            innerGroups.Add(innerGroup);
                        }
                    }
                }

                if (!innerGroups.Any())
                {
                    Utils.DebugPrint("This unified group has no inner groups. Skipping this unified group.");
                    continue;
                }

                var primaryGroup = innerGroups.FirstOrDefault(x => x.ID == entry.UnifiedPrimaryBubbleGroupGuid);
                if (primaryGroup == null)
                {
                    Utils.DebugPrint("Unified group, primary group " + entry.UnifiedPrimaryBubbleGroupGuid +
                                     " could not be related. Skipping this unified group.");
                    continue;
                }

                var id = entry.Guid;

                var unifiedGroup = BubbleGroupFactory.CreateUnifiedInternal(innerGroups, primaryGroup, id);

                var sendingGroup = innerGroups.FirstOrDefault(x => x.ID == entry.UnifiedSendingBubbleGroupGuid);
                if (sendingGroup != null)
                {
                    unifiedGroup._sendingGroup = sendingGroup;
                }

                BubbleGroupManager.BubbleGroupsAdd(unifiedGroup, true);
            }

            return(true);
        }
        internal static void LoadAllPartiallyIfPossible(int bubblesPerGroup = 100)
        {
            var corruptedGroups = new List <string>();

            var bubbleGroupsLocations       = Directory.GetFiles(BubbleGroupDatabase.GetBaseLocation(), "*.*", SearchOption.AllDirectories);
            var bubbleGroupsLocationsSorted =
                bubbleGroupsLocations.OrderByDescending(x => Time.GetUnixTimestamp(File.GetLastWriteTime(x))).ToList();

            foreach (var bubbleGroupLocation in bubbleGroupsLocationsSorted)
            {
                String groupId = null;
                try
                {
                    var groupHeader = Path.GetFileNameWithoutExtension(bubbleGroupLocation);

                    var groupDelimeter = groupHeader.IndexOf("^", StringComparison.Ordinal);
                    var serviceName    = groupHeader.Substring(0, groupDelimeter);
                    groupId = groupHeader.Substring(groupDelimeter + 1);

                    var service = ServiceManager.GetByName(serviceName);

                    if (service == null)
                    {
                        Utils.DebugPrint("Service " + serviceName +
                                         " not found in AllServices!");
                        continue;
                    }

                    var deserializedBubbleGroup = LoadPartiallyIfPossible(bubbleGroupLocation, service, groupId, bubblesPerGroup);
                    if (deserializedBubbleGroup == null)
                    {
                        throw new Exception("DeserializedBubbleGroup is nothing.");
                    }
                    BubbleGroupManager.BubbleGroupsAdd(deserializedBubbleGroup);
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint("Group " + bubbleGroupLocation + " is corrupt. Deleting. " + ex);
                    File.Delete(bubbleGroupLocation);
                    if (groupId != null)
                    {
                        corruptedGroups.Add(groupId);
                    }
                }
            }

            var migrationResaveNeeded  = false;
            var corruptedUnifiedGroups =
                new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>();
            var removeFromRuntime =
                new List <SimpleDatabase <UnifiedBubbleGroup, DisaUnifiedBubbleGroupEntry> .Container>();

            foreach (var group in UnifiedBubbleGroupsDatabase)
            {
                var innerGroupCorrupt = false;
                var innerGroups       = new List <BubbleGroup>();
                foreach (var innerGroupId in @group.Serializable.GroupIds)
                {
                    var innerGroup = BubbleGroupManager.Find(innerGroupId);
                    if (innerGroup == null)
                    {
                        Utils.DebugPrint("Unified group, inner group " + innerGroupId +
                                         " could not be related.");
                        if (corruptedGroups.Contains(innerGroupId))
                        {
                            Utils.DebugPrint(
                                "It was detected that this inner group was corrupted and deleted. Will delete unified group.");
                            innerGroupCorrupt = true;
                            corruptedUnifiedGroups.Add(@group);
                        }
                    }
                    else
                    {
                        innerGroups.Add(innerGroup);
                    }
                }
                if (!innerGroups.Any())
                {
                    Utils.DebugPrint("Yuck. This unified group has no inner groups. Removing from runtime.");
                    removeFromRuntime.Add(@group);
                    continue;
                }
                if (innerGroupCorrupt)
                {
                    continue;
                }

                var primaryGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.PrimaryGroupId);
                if (primaryGroup == null)
                {
                    Utils.DebugPrint("Unified group, primary group " + @group.Serializable.PrimaryGroupId +
                                     " could not be related. Removing from runtime.");
                    removeFromRuntime.Add(@group);
                    continue;
                }

                var id = @group.Serializable.Id;

                var unifiedGroup = CreateUnifiedInternal(innerGroups, primaryGroup, id);
                if (id == null)
                {
                    migrationResaveNeeded  = true;
                    @group.Serializable.Id = unifiedGroup.ID;
                }

                var sendingGroup = innerGroups.FirstOrDefault(x => x.ID == @group.Serializable.SendingGroupId);
                if (sendingGroup != null)
                {
                    unifiedGroup.SendingGroup = sendingGroup;
                }

                @group.Object = unifiedGroup;
                BubbleGroupManager.BubbleGroupsAdd(unifiedGroup);
            }
            if (removeFromRuntime.Any())
            {
                foreach (var group in removeFromRuntime)
                {
                    UnifiedBubbleGroupsDatabase.Remove(@group);
                }
            }
            if (corruptedUnifiedGroups.Any())
            {
                foreach (var group in corruptedUnifiedGroups)
                {
                    UnifiedBubbleGroupsDatabase.Remove(@group);
                }
            }
            if (migrationResaveNeeded)
            {
                Utils.DebugPrint("It was detected that we need to save migration changes for UnifiedBubbleGroups.");
                UnifiedBubbleGroupsDatabase.SaveChanges();
            }

            try
            {
                foreach (var groupCache in BubbleGroupCacheManager.Load())
                {
                    var associatedGroup = BubbleGroupManager.Find(groupCache.Guid);
                    if (associatedGroup == null)
                    {
                        continue;
                    }

                    var unifiedGroup = associatedGroup as UnifiedBubbleGroup;
                    if (unifiedGroup != null)
                    {
                        associatedGroup = unifiedGroup.PrimaryGroup;
                    }

                    associatedGroup.Title = groupCache.Name;
                    associatedGroup.Photo = groupCache.Photo;
                    associatedGroup.IsPhotoSetInitiallyFromCache = true;
                    if (groupCache.Participants != null)
                    {
                        associatedGroup.Participants = groupCache.Participants.ToSynchronizedCollection();
                        foreach (var participant in associatedGroup.Participants)
                        {
                            participant.IsPhotoSetInitiallyFromCache = true;
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                Utils.DebugPrint("Failed to load bubble groups!: " + ex);
            }

            BubbleGroupSettingsManager.Load();
        }
Example #8
0
        internal static BubbleGroup Group(VisualBubble vb, bool resend = false, bool insertAtBottom = false)
        {
            lock (BubbleGroupDatabase.OperationLock)
            {
                Utils.DebugPrint("Grouping an " + vb.Direction + " bubble on service " + vb.Service.Information.ServiceName);

                AddUrlMarkupIfNeeded(vb);

                var theGroup =
                    BubbleGroupManager.FindWithAddress(vb.Service, vb.Address);

                BubbleGroupFactory.LoadFullyIfNeeded(theGroup);

                var duplicate = false;
                var newGroup  = false;
                if (theGroup == null)
                {
                    Utils.DebugPrint(vb.Service.Information.ServiceName + " unable to find suitable group. Creating a new one.");

                    theGroup = new BubbleGroup(vb, null, false);

                    newGroup = true;

                    Utils.DebugPrint("GUID of new group: " + theGroup.ID);

                    BubbleGroupSettingsManager.SetUnreadIndicatorGuid(theGroup, theGroup.LastBubbleSafe().ID, true);

                    vb.Service.NewBubbleGroupCreated(theGroup).ContinueWith(x =>
                    {
                        // force the UI to refetch the photo
                        theGroup.IsPhotoSetFromService = false;
                        SendSubscribe(theGroup, true);
                        BubbleGroupUpdater.Update(theGroup);
                    });

                    BubbleGroupManager.BubbleGroupsAdd(theGroup);
                }
                else
                {
                    if (resend)
                    {
                        if (vb.Status == Bubble.BubbleStatus.Failed)
                        {
                            UpdateStatus(vb, Bubble.BubbleStatus.Waiting, theGroup);
                        }
                        return(theGroup);
                    }

                    // Does the Service for this VisualBubble require that the VisualBubble's IdService and IdService2
                    // be distinct?
                    var visualBubbleServiceId = vb.Service as IVisualBubbleServiceId;
                    if (visualBubbleServiceId != null &&
                        visualBubbleServiceId.DisctinctIncomingVisualBubbleIdServices())
                    {
                        // Ok, we need to be distinct, BUT do the VisualBubble Type's have to be distinct as well?
                        var checkType = true;
                        if (!DisaFrameworkMethods.Missing(vb.Service, DisaFrameworkMethods.IVisualBubbleServiceIdCheckType))
                        {
                            checkType = visualBubbleServiceId.CheckType();
                        }

                        // Ok, now does the Service have special additional logic it wants to use for the distinction comparison?
                        // Example: For Telegram, we allow an ImageBubble immediately followed by a TextBubble to have the
                        //          same VisualBubble.IdService - as this represents an image with a caption in Telegram.
                        if (DisaFrameworkMethods.Missing(vb.Service, DisaFrameworkMethods.IVisualBubbleServiceIdVisualBubbleIdComparer))
                        {
                            // Normal distinction checks
                            if (vb.IdService != null)
                            {
                                duplicate = theGroup.Bubbles.FirstOrDefault(x =>
                                                                            (!checkType || x.GetType() == vb.GetType()) && x.IdService == vb.IdService) != null;
                            }
                            if (!duplicate && vb.IdService2 != null)
                            {
                                duplicate = theGroup.Bubbles.FirstOrDefault(x =>
                                                                            (!checkType || x.GetType() == vb.GetType()) && x.IdService2 == vb.IdService2) != null;
                            }
                        }
                        else
                        {
                            // Special additional Service defined distinction checks
                            if (vb.IdService != null)
                            {
                                var duplicateBubble = theGroup.Bubbles.FirstOrDefault(x =>
                                                                                      (!checkType || x.GetType() == vb.GetType()) && x.IdService == vb.IdService);

                                duplicate = duplicateBubble == null ? false :
                                            visualBubbleServiceId.VisualBubbleIdComparer(left: duplicateBubble, right: vb);
                            }
                            if (!duplicate && vb.IdService2 != null)
                            {
                                var duplicateBubble = theGroup.Bubbles.FirstOrDefault(x =>
                                                                                      (!checkType || x.GetType() == vb.GetType()) && x.IdService2 == vb.IdService2);

                                duplicate = duplicateBubble == null ? false :
                                            visualBubbleServiceId.VisualBubbleIdComparer(duplicateBubble, vb);
                            }
                        }
                    }

                    if (!duplicate)
                    {
                        Utils.DebugPrint(vb.Service.Information.ServiceName + " found a group. Adding.");

                        if (insertAtBottom)
                        {
                            var lastBubble = theGroup.LastBubbleSafe();
                            if (lastBubble.Time > vb.Time)
                            {
                                vb.Time = lastBubble.Time;
                            }
                        }

                        theGroup.InsertByTime(vb);
                    }
                    else
                    {
                        Utils.DebugPrint("Yuck. It's a duplicate bubble. No need to readd: " + vb.IdService + ", " + vb.IdService2);
                    }
                }

                try
                {
                    if (theGroup.IsParty && !string.IsNullOrWhiteSpace(vb.ParticipantAddressNickname))
                    {
                        var participantAddressNicknamesArray = theGroup.ParticipantNicknames;
                        if (participantAddressNicknamesArray == null)
                        {
                            participantAddressNicknamesArray = new DisaParticipantNickname[0];
                        }
                        var participantAddressNicknames = participantAddressNicknamesArray.ToList();
                        var changed = false;
                        var adding  = true;
                        foreach (var participantAddressNickname in participantAddressNicknames)
                        {
                            if (theGroup.Service.BubbleGroupComparer(participantAddressNickname.Address, vb.ParticipantAddress))
                            {
                                if (participantAddressNickname.Nickname != vb.ParticipantAddressNickname)
                                {
                                    participantAddressNickname.Nickname = vb.ParticipantAddressNickname;
                                    changed = true;
                                }
                                adding = false;
                                break;
                            }
                        }
                        if (adding)
                        {
                            participantAddressNicknames.Add(new DisaParticipantNickname
                            {
                                Address  = vb.ParticipantAddress,
                                Nickname = vb.ParticipantAddressNickname,
                            });
                        }
                        if (changed || adding)
                        {
                            theGroup.ParticipantNicknames = participantAddressNicknames.ToArray();
                        }
                    }
                }
                catch (Exception ex)
                {
                    Utils.DebugPrint("Failed to insert/update participant nickname into cache: " + ex);
                }

                if (!duplicate)
                {
                    Utils.DebugPrint("Inserting bubble into database group!");

                    try
                    {
                        if (newGroup)
                        {
                            BubbleGroupDatabase.AddBubble(theGroup, vb);
                        }
                        else
                        {
                            BubbleGroupDatabase.InsertBubbleByTime(theGroup, vb);
                        }
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint("Bubble failed to be inserting/added into the group " + theGroup.ID + ": " + ex);
                    }

                    try
                    {
                        BubbleGroupEvents.RaiseBubbleInserted(vb, theGroup);
                    }
                    catch (Exception ex)
                    {
                        Utils.DebugPrint(
                            "Error in notifying the interface that the bubble group has been updated (" +
                            vb.Service.Information.ServiceName + "): " + ex.Message);
                    }
                }

                return(theGroup);
            }
        }