internal static async void UpdatePartyParticipants(BubbleGroup bubbleGroup, Action finished = null) { var service = bubbleGroup.Service; if (!ServiceManager.IsRunning(service)) { return; } if (!bubbleGroup.IsParty) { if (finished != null) { finished(); } return; } try { await service.GetBubbleGroupPartyParticipants(bubbleGroup, participants => { // we need to propogate the old participant photos to the new participant list var newParticipants = participants == null ? new List <DisaParticipant>() : participants.ToList(); foreach (var oldParticipant in bubbleGroup.Participants) { var newParticipant = newParticipants.FirstOrDefault(x => service.BubbleGroupComparer(x.Address, oldParticipant.Address)); if (newParticipant != null) { newParticipant.Photo = oldParticipant.Photo; newParticipant.IsPhotoSetFromService = oldParticipant.IsPhotoSetFromService; } } // move all unknown participants over // if there are more than 100 unknown participants, we'll ignore and force unknown participants to be rebuilt // to prevent an exceedingly large cache if (bubbleGroup.Participants.Count(x => x.Unknown) < 100) { foreach (var unknownParticipant in bubbleGroup.Participants.Where(x => x.Unknown)) { if (newParticipants.FirstOrDefault(x => service.BubbleGroupComparer(x.Address, unknownParticipant.Address)) == null) { newParticipants.Add(unknownParticipant); } } } bubbleGroup.Participants = new ThreadSafeList <DisaParticipant>(newParticipants); bubbleGroup.IsParticipantsSetFromService = true; if (finished == null) { BubbleGroupEvents.RaiseBubblesUpdated(bubbleGroup); } else { finished(); } }); } catch (Exception ex) { Utils.DebugPrint("Error updating bubble group participants: " + service.Information.ServiceName + " - " + bubbleGroup.Address + ": " + ex); if (finished != null) { finished(); } } }
public static bool InsertBubbleByTime(BubbleGroup group, VisualBubble bubble, int maxDepth = 1000) { return(InsertBubblesByTime(@group, new [] { bubble }, maxDepth)); }
public static bool UpdateBubble(BubbleGroup group, VisualBubble bubble, int searchDepth = 100) { return(UpdateBubble(@group, new[] { bubble }, searchDepth)); }
private static List <VisualBubble> LoadBubblesIntoQueryAgent(Agent innerGroupAgent, BubbleGroup innerGroup, long currentTime, int maxLoadPerGroup, LoadBubblesIntoStateHolder innerGroupState) { try { var task = innerGroupAgent.LoadBubbles(innerGroup, currentTime, maxLoadPerGroup); task.Wait(); var loadedBubbles = task.Result; if (loadedBubbles == null || loadedBubbles.Count == 0) { innerGroupState.Dead = true; return(null); } else { return(loadedBubbles); } } catch (Exception ex) { Utils.DebugPrint("Loading bubbles for service " + innerGroup.Service.Information.ServiceName + " on group " + innerGroup.ID + " failed: " + ex); innerGroupState.Dead = true; } return(null); }
private static IEnumerable <bool> LoadBubblesInto(BubbleGroup group, List <VisualBubble> listToLoadInto, LoadBubbles instance) { if (!listToLoadInto.Any()) { yield break; } Utils.DebugPrint("Loading into convo " + group.ID); lock (SyncLock) { Utils.DebugPrint("Loading into convo " + group.ID + " (after lock)"); var unifiedGroup = group as UnifiedBubbleGroup; var groups = unifiedGroup != null ? unifiedGroup.Groups : new List <BubbleGroup>() { group }; const int MaxLoadPerGroup = 100; var currentTime = listToLoadInto.Min(x => x.Time); var bubbleGroupStates = new Dictionary <BubbleGroup, LoadBubblesIntoStateHolder>(); foreach (var innerGroup in groups) { bubbleGroupStates[innerGroup] = new LoadBubblesIntoStateHolder(); } EventHandler <Service> serviceStarted = (sender, e) => { foreach (var state in bubbleGroupStates) { if (state.Key.Service == e) { state.Value.Dead = false; } } }; ServiceEvents.Started += serviceStarted; instance.ServiceStarted = serviceStarted; while (true) { var listToLoadIntoCount = listToLoadInto.Count; var bubbleLoads = new Dictionary <BubbleGroup, List <Tuple <VisualBubble, bool> > >(); foreach (var innerGroup in groups) { var innerGroupState = bubbleGroupStates[innerGroup]; if (innerGroupState.Dead) { bubbleLoads[innerGroup] = null; continue; } if (innerGroupState.LocalFinished) { var agentBubbles = LoadBubblesIntoDoAgent(innerGroup, currentTime, MaxLoadPerGroup, innerGroupState); if (agentBubbles != null) { bubbleLoads[innerGroup] = agentBubbles.Select(x => new Tuple <VisualBubble, bool>(x, true)).ToList(); } else { bubbleLoads[innerGroup] = null; } } else { var localBubbles = new List <VisualBubble>(); innerGroupState.LocalCursor = BubbleGroupDatabase.FetchBubblesAt(innerGroup, currentTime, MaxLoadPerGroup, ref localBubbles, innerGroupState.LocalCursor); if (innerGroupState.LocalCursor == 0 || localBubbles.Count == 0) { innerGroupState.LocalFinished = true; } if (localBubbles.Count == MaxLoadPerGroup) { bubbleLoads[innerGroup] = localBubbles.Select(x => new Tuple <VisualBubble, bool>(x, false)).ToList(); } else { var innerGroupComparer = innerGroup.Service as Comparer; if (innerGroupComparer != null) { var agentBubbles = LoadBubblesIntoDoAgent(innerGroup, currentTime, MaxLoadPerGroup, innerGroupState); if (agentBubbles == null) { bubbleLoads[innerGroup] = localBubbles.Select(x => new Tuple <VisualBubble, bool>(x, false)).ToList(); } else { var innerGroupAgent = innerGroup.Service as Agent; var combined = new List <Tuple <VisualBubble, bool> >(); // combine them: take all agent bubbles, and then try to replace the clouds with the locals. // what can't be replaced becomes the inserts. for (int i = 0; i < agentBubbles.Count; i++) { var agentBubble = agentBubbles[i]; var localBubble = localBubbles.FirstOrDefault(x => innerGroupComparer.LoadBubblesComparer(x, agentBubble)); if (localBubble != null) { combined.Add(new Tuple <VisualBubble, bool>(localBubble, false)); } else { combined.Add(new Tuple <VisualBubble, bool>(agentBubble, true)); } } bubbleLoads[innerGroup] = combined; } } else { bubbleLoads[innerGroup] = localBubbles.Select(x => new Tuple <VisualBubble, bool>(x, false)).ToList(); } } } } // if all the sync agents failed, then obviously loading more bubbles in trivially failed if (bubbleGroupStates.Count(x => x.Value.Dead) == groups.Count) { yield break; } // insert the bubbles into the disa bubble group with two conditions // a) must not be bubble retreived from disa bubble group // b) must not be a duplicate already in the list to load into var listToLoadIntoBubblesOnTime = listToLoadInto.Where(x => x.Time == currentTime).ToList(); foreach (var bubbleLoad in bubbleLoads) { if (bubbleLoad.Value == null) { continue; } var bubbleGroup = bubbleLoad.Key; var bubblesToInsert = LoadBubblesIntoRemoveDuplicates(bubbleLoad.Value.Where(x => x.Item2) .Select(x => x.Item1).ToList(), listToLoadIntoBubblesOnTime); if (bubblesToInsert.Any()) { BubbleGroupDatabase.InsertBubblesByTime(bubbleGroup, bubblesToInsert.ToArray(), int.MaxValue, true, true); } } // find the greatest minimum time of all the bubble loads // and merge the bubble loads against that var greatestMin = 0L; foreach (var bubbleLoad in bubbleLoads) { if (bubbleLoad.Value == null || !bubbleLoad.Value.Any()) { continue; } var min = bubbleLoad.Value.Min(x => x.Item1.Time); if (min > greatestMin) { greatestMin = min; } } var mergedBubbles = new List <VisualBubble>(); foreach (var bubbleLoad in bubbleLoads) { if (bubbleLoad.Value != null) { var bubblesToMerge = bubbleLoad.Value.Where(x => x.Item1.Time >= greatestMin).Select(x => x.Item1).ToList(); foreach (var bubbleToMerge in bubblesToMerge) { bubbleToMerge.BubbleGroupReference = bubbleLoad.Key; } mergedBubbles.AddRange(bubblesToMerge); } } mergedBubbles.TimSort((x, y) => x.Time.CompareTo(y.Time)); // insert the merged bubbles into the list to load into, making sure to // remove and duplicates encountered. listToLoadInto.InsertRange(0, mergedBubbles); LoadBubblesIntoRemoveDuplicates(listToLoadInto, currentTime); currentTime = mergedBubbles.First().Time; // if the count wasn't altered, we've hit the end if (listToLoadIntoCount == listToLoadInto.Count) { yield break; } yield return(true); } } }
public static IEnumerable <bool> LoadBubblesIntoOffline(BubbleGroup group, List <VisualBubble> listToLoadInto) { if (!listToLoadInto.Any()) { yield break; } Utils.DebugPrint("Loading into convo (offline): " + group.ID); var unifiedGroup = group as UnifiedBubbleGroup; var groups = unifiedGroup != null ? unifiedGroup.Groups : new List <BubbleGroup>() { group }; const int MaxLoadPerGroup = 100; var currentTime = listToLoadInto.Min(x => x.Time); var bubbleGroupStates = new Dictionary <BubbleGroup, LoadBubblesIntoStateHolder>(); foreach (var innerGroup in groups) { bubbleGroupStates[innerGroup] = new LoadBubblesIntoStateHolder(); } while (true) { var listToLoadIntoCount = listToLoadInto.Count; var bubbleLoads = new Dictionary <BubbleGroup, List <Tuple <VisualBubble, bool> > >(); foreach (var innerGroup in groups) { var innerGroupState = bubbleGroupStates[innerGroup]; if (innerGroupState.LocalFinished) { bubbleLoads[innerGroup] = null; } else { var localBubbles = new List <VisualBubble>(); innerGroupState.LocalCursor = BubbleGroupDatabase.FetchBubblesAt(innerGroup, currentTime, MaxLoadPerGroup, ref localBubbles, innerGroupState.LocalCursor); if (innerGroupState.LocalCursor == 0 || localBubbles.Count == 0) { innerGroupState.LocalFinished = true; } bubbleLoads[innerGroup] = localBubbles.Select(x => new Tuple <VisualBubble, bool>(x, false)).ToList(); } } // find the greatest minimum time of all the bubble loads // and merge the bubble loads against that var greatestMin = 0L; foreach (var bubbleLoad in bubbleLoads) { if (bubbleLoad.Value == null || !bubbleLoad.Value.Any()) { continue; } var min = bubbleLoad.Value.Min(x => x.Item1.Time); if (min > greatestMin) { greatestMin = min; } } var mergedBubbles = new List <VisualBubble>(); foreach (var bubbleLoad in bubbleLoads) { if (bubbleLoad.Value != null) { var bubblesToMerge = bubbleLoad.Value.Where(x => x.Item1.Time >= greatestMin).Select(x => x.Item1).ToList(); foreach (var bubbleToMerge in bubblesToMerge) { bubbleToMerge.BubbleGroupReference = bubbleLoad.Key; } mergedBubbles.AddRange(bubblesToMerge); } } mergedBubbles.TimSort((x, y) => x.Time.CompareTo(y.Time)); // insert the merged bubbles into the list to load into, making sure to // remove and duplicates encountered. listToLoadInto.InsertRange(0, mergedBubbles); LoadBubblesIntoRemoveDuplicates(listToLoadInto, currentTime); if (mergedBubbles.Any()) { currentTime = mergedBubbles.First().Time; } // if the count wasn't altered, we've hit the end if (listToLoadIntoCount == listToLoadInto.Count) { yield break; } yield return(true); } }
public abstract bool SwitchCurrentBubbleGroupOnUI(BubbleGroup group);
public abstract Task GetBubbleGroupLegibleId(BubbleGroup group, Action <string> result);
public abstract Task GetBubbleGroupName(BubbleGroup group, Action <string> result);
public override Task GetBubbleGroupUnknownPartyParticipant(BubbleGroup group, string unknownPartyParticipant, Action <DisaParticipant> result) { throw new NotImplementedException(); }
public override Task GetBubbleGroupLastOnline(BubbleGroup @group, Action <long> result) { return(Task.Factory.StartNew(() => result(0))); }
public override Task GetBubbleGroupPartyParticipants(BubbleGroup @group, Action <DisaParticipant[]> result) { throw new NotImplementedException(); }
public override Task GetBubbleGroupPhoto(BubbleGroup @group, Action <DisaThumbnail> result) { return(Task.Factory.StartNew(() => result(null))); }
public override Task GetBubbleGroupName(BubbleGroup @group, Action <string> result) { return(Task.Factory.StartNew(() => result(Name))); }
public static void RemoveUnread(BubbleGroup group, bool updateUi = true) { SetUnread(@group, false, updateUi); }
public abstract Task GetBubbleGroupPhoto(BubbleGroup group, Action <DisaThumbnail> result);
public static void SetReadTimes(BubbleGroup group, DisaReadTime[] readTimes) { @group.ReadTimes = readTimes == null ? null : (!readTimes.Any() ? null : readTimes); }
public abstract Task GetBubbleGroupPartyParticipants(BubbleGroup group, Action <DisaParticipant[]> result);
public static Task Sync(BubbleGroup group, bool force = false) { return(Task.Factory.StartNew(() => { Utils.DebugPrint("Syncing convo " + group.ID); lock (SyncLock) { Utils.DebugPrint("Syncing convo " + group.ID + " (after lock)"); var somethingSynced = false; var groupsToSync = new List <BubbleGroup>(); var unifiedGroup = group as UnifiedBubbleGroup; if (unifiedGroup != null) { groupsToSync.AddRange(unifiedGroup.Groups); } else { groupsToSync.Add(group); } foreach (var groupToSync in groupsToSync) { if (!groupToSync.NeedsSync && !force) { continue; } var groupToSyncAgent = groupToSync.Service as Agent; if (groupToSyncAgent == null) { continue; } if (!ServiceManager.IsRunning(groupToSync.Service)) { continue; } somethingSynced = true; try { ReSync: var syncTime = Time.GetNowUnixTimestamp(); var syncTask = groupToSyncAgent.Sync(groupToSync, Database.GetActionId(groupToSync)); syncTask.Wait(); var syncResult = syncTask.Result; if (!syncResult.EmptyResult && !syncResult.NullActionId && !syncResult.JustRefresh) { lock (BubbleGroupDatabase.OperationLock) { if (syncResult.ResultType == Result.Type.Purge) { if (BubbleGroupManager.LastBubbleSentTimestamps.ContainsKey(groupToSync.ID)) { lock (BubbleGroupManager.LastBubbleSentTimestamps) { var lastBubbleSentTime = BubbleGroupManager.LastBubbleSentTimestamps[groupToSync.ID]; if (lastBubbleSentTime >= syncTime) { Utils.DebugPrint("Sync has detected that a bubble was sent during the time the sync was fetched. Redoing sync..."); goto ReSync; } } } Utils.DebugPrint("Sync is purging the database for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleGroupDatabase.Kill(groupToSync); } if (!syncResult.EmptyInserts) { Utils.DebugPrint("Sync is inserting " + syncResult.Inserts.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); if (syncResult.ResultType == Result.Type.Purge) { syncResult.Inserts.TimSort((x, y) => x.Time.CompareTo(y.Time)); BubbleGroupDatabase.AddBubbles(groupToSync, syncResult.Inserts); // If a purge is issued, then we need to carry over all sending bubbles to the new BubbleGroup being created. // Additionally, we need to remove any sending bubbles that are already on the insert list to avoid duplicates. var sendingBubbles = BubbleManager.FetchAllSending(groupToSync).ToList(); var sendingBubblesFiltered = new List <VisualBubble>(); foreach (var sendingBubble in sendingBubbles) { var hasInsertBubble = syncResult.Inserts.FirstOrDefault(x => x.ID == sendingBubble.ID) != null; if (!hasInsertBubble) { sendingBubblesFiltered.Add(sendingBubble); } } if (sendingBubblesFiltered.Any()) { BubbleGroupDatabase.InsertBubblesByTime(groupToSync, sendingBubblesFiltered.ToArray(), SearchDepth); } } else { BubbleGroupDatabase.InsertBubblesByTime(groupToSync, syncResult.Inserts, SearchDepth); } } if (!syncResult.EmptyUpdates) { Utils.DebugPrint("Sync is updating " + syncResult.Updates.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleManager.Update(groupToSync, syncResult.Updates, SearchDepth); } } } else { Utils.DebugPrint("Sync for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + " returned an empty result (" + syncResult.ResultType.ToString() + ")."); } if (!syncResult.NullActionId) { Database.SetActionId(groupToSync, syncResult.NewActionId); } groupToSync.NeedsSync = false; } catch (Exception ex) { Utils.DebugPrint("Failed to sync bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + ": " + ex); } } if (somethingSynced) { lock (BubbleGroupDatabase.OperationLock) { // Here we need to replace all the sending or downloading bubbles with the 'real' ones. // If this is never done, then on the UI, a 'sending' will never be set to 'sent', and any download // progress will never be updated. Why? Different memory objects. var sendingBubbles = BubbleManager.FetchAllSendingAndDownloading(group).ToList(); BubbleGroupFactory.UnloadFullLoad(group); BubbleGroupFactory.LoadFullyIfNeeded(group, true); if (sendingBubbles.Any()) { BubbleManager.Replace(group, sendingBubbles); } group.RaiseBubblesSynced(); } } } })); }
public abstract Task GetBubbleGroupUnknownPartyParticipant(BubbleGroup group, string unknownPartyParticipant, Action <DisaParticipant> result);
public static IEnumerable <VisualBubble> ReadBubblesFromDatabase(BubbleGroup group) { return(BubbleGroupDatabase.FetchBubbles(group, SearchDepth, false)); }
public abstract Task GetBubbleGroupLastOnline(BubbleGroup group, Action <long> result);
public LoadBubbles(BubbleGroup group, List <VisualBubble> listToLoadInto) { _enumerator = LoadBubblesInto(group, listToLoadInto, this).GetEnumerator(); }
public virtual Task OpenedBubbleGroup(BubbleGroup group) { return(Task.FromResult(0)); }
public static Task Sync(BubbleGroup group, bool force = false) { return(Task.Factory.StartNew(() => { Utils.DebugPrint("Syncing convo " + group.ID); lock (SyncLock) { Utils.DebugPrint("Syncing convo " + group.ID + " (after lock)"); var somethingSynced = true; var groupsToSync = new List <BubbleGroup>(); var unifiedGroup = group as UnifiedBubbleGroup; if (unifiedGroup != null) { groupsToSync.AddRange(unifiedGroup.Groups); } else { groupsToSync.Add(group); } foreach (var groupToSync in groupsToSync) { if (!groupToSync.NeedsSync && !force) { continue; } var groupToSyncAgent = groupToSync.Service as Agent; if (groupToSyncAgent == null) { continue; } if (!ServiceManager.IsRunning(groupToSync.Service)) { continue; } try { var syncTask = groupToSyncAgent.Sync(groupToSync, Database.GetActionId(groupToSync)); syncTask.Wait(); var syncResult = syncTask.Result; if (!syncResult.EmptyResult && !syncResult.NullActionId && !syncResult.JustRefresh) { lock (BubbleGroupDatabase.OperationLock) { if (syncResult.ResultType == Result.Type.Purge) { Utils.DebugPrint("Sync is purging the database for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleGroupDatabase.Kill(groupToSync); } if (!syncResult.EmptyInserts) { Utils.DebugPrint("Sync is inserting " + syncResult.Inserts.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); if (syncResult.ResultType == Result.Type.Purge) { syncResult.Inserts.TimSort((x, y) => x.Time.CompareTo(y.Time)); BubbleGroupDatabase.AddBubbles(groupToSync, syncResult.Inserts); } else { BubbleGroupDatabase.InsertBubblesByTime(groupToSync, syncResult.Inserts, SearchDepth); } } if (!syncResult.EmptyUpdates) { Utils.DebugPrint("Sync is updating " + syncResult.Updates.Length + " bubbles into " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName); BubbleManager.Update(groupToSync, syncResult.Updates, SearchDepth); } Database.SetActionId(groupToSync, syncResult.NewActionId); } } else { Utils.DebugPrint("Sync for bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + " returned an empty result (" + syncResult.ResultType.ToString() + ")."); somethingSynced = syncResult.JustRefresh; } groupToSync.NeedsSync = false; } catch (Exception ex) { Utils.DebugPrint("Failed to sync bubble group " + groupToSync.ID + " on service " + groupToSync.Service.Information.ServiceName + ": " + ex); somethingSynced = false; } } if (somethingSynced) { lock (BubbleGroupDatabase.OperationLock) { var sendingBubbles = BubbleManager.FetchAllSendingAndDownloading(group).ToList(); BubbleGroupFactory.UnloadFullLoad(group); BubbleGroupFactory.LoadFullyIfNeeded(group, true); if (sendingBubbles.Any()) { BubbleManager.Replace(group, sendingBubbles); } group.RaiseBubblesSynced(); } } } })); }
public static BubbleGroup GetSending(BubbleGroup group) { var unifiedGroup = @group as UnifiedBubbleGroup; return(unifiedGroup == null ? @group : unifiedGroup.SendingGroup); }
public static bool InsertBubblesByTime(BubbleGroup group, VisualBubble[] bubbles, int maxDepth = 1000, bool guidCheck = false, bool insertAtTop = false) { //we can't operate if a thread/worker is concurrently processing a new bubble lock (OperationLock) { var groupDatabaseLocation = GetLocation(@group); var bubbleTuples = bubbles.Select(x => new Tuple <long, VisualBubble>(x.Time, x)).ToList(); VisualBubble lastBubble = null; long insertPosition = -1; using (var stream = File.Open(groupDatabaseLocation, FileMode.Open, FileAccess.ReadWrite)) { stream.Seek(stream.Length, SeekOrigin.Begin); for (var i = 0; i < (maxDepth != -1 ? maxDepth : Int32.MaxValue); i++) { if (stream.Position == 0) { if (insertAtTop) { insertPosition = 0; var cut = new byte[(int)stream.Length]; stream.Read(cut, 0, (int)stream.Length); stream.Position = 0; var bubblesToInsert = bubbleTuples.Select(x => x.Item2).ToList(); bubblesToInsert.TimSort((x, y) => x.Time.CompareTo(y.Time)); foreach (var bubbleToInsert in bubblesToInsert) { using (var ms = new MemoryStream()) { Serializer.Serialize(ms, bubbleToInsert); var bubbleToInsertData = ms.ToArray(); var bubbleToInsertHeader = bubbleToInsert.GetType().Name + ":" + bubbleToInsert.ID + ":" + bubbleToInsert.Time; BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleToInsertData); BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleToInsertHeader); } } stream.Write(cut, 0, cut.Length); stream.SetLength(stream.Position); } else { break; } } byte[] headerBytes; int headerBytesLength; int endPosition; BubbleGroupDatabasePrimitives.ReadBubbleHeader(stream, out headerBytes, out headerBytesLength); BubbleGroupDatabasePrimitives.FindBubbleHeaderDelimiter(headerBytes, headerBytesLength, 0, out endPosition); var bubbleDataLength = BubbleGroupDatabasePrimitives.JumpBubbleData(stream); //we need to seek over the data. var guid = BubbleGroupDatabasePrimitives.ReadBubbleHeaderStruct(headerBytes, headerBytesLength, endPosition + 1, out endPosition); var time = BubbleGroupDatabasePrimitives.AsciiBytesToString(headerBytes, endPosition + 1, headerBytesLength); long longTime; Int64.TryParse(time, out longTime); { var bubblesToInsert = bubbleTuples.Where(x => { if (guidCheck && guid == x.Item2.ID) { return(false); } if (longTime <= x.Item1) { return(true); } return(false); }).ToList(); if (!bubblesToInsert.Any()) { continue; } var bubbleSize = headerBytesLength + bubbleDataLength + 8; var insertLocation = stream.Position + bubbleSize; stream.Seek(insertLocation, SeekOrigin.Begin); var cutLength = stream.Length - insertLocation; var cut = new byte[cutLength]; stream.Read(cut, 0, (int)cutLength); //should always work as long as the count ain't crazy high stream.Seek(insertLocation, SeekOrigin.Begin); insertPosition = stream.Position; foreach (var bubbleToInsert in bubblesToInsert.Select(x => x.Item2)) { using (var ms = new MemoryStream()) { Serializer.Serialize(ms, bubbleToInsert); var bubbleToInsertData = ms.ToArray(); var bubbleToInsertHeader = bubbleToInsert.GetType().Name + ":" + bubbleToInsert.ID + ":" + bubbleToInsert.Time; BubbleGroupDatabasePrimitives.WriteBubbleData(stream, bubbleToInsertData); BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, bubbleToInsertHeader); } } stream.Write(cut, 0, cut.Length); stream.SetLength(stream.Position); // adding to end if (cut.Length == 0) { lastBubble = bubblesToInsert.Last().Item2; } foreach (var bubbleToInsert in bubblesToInsert) { bubbleTuples.Remove(bubbleToInsert); } if (!bubbleTuples.Any()) { break; } } } BubbleGroupIndex.UpdateLastBubbleOrAndLastModifiedIndex(group.ID, lastBubble, insertPosition); } if (!bubbleTuples.Any()) { return(true); } return(false); } }
public static bool IsUnified(BubbleGroup group) { return(@group as UnifiedBubbleGroup != null); }
public static bool UpdateBubble(BubbleGroup group, VisualBubble[] bubbles, int searchDepth = 100) { //we can't operate if a thread/worker is concurrently processing a new bubble lock (OperationLock) { var groupDatabaseLocation = GetLocation(@group); var bubbleTuples = bubbles.Select(x => new Tuple <string, VisualBubble>(x.ID, x)).ToList(); VisualBubble lastBubble = null; long insertPosition = -1; using (var stream = File.Open(groupDatabaseLocation, FileMode.Open, FileAccess.ReadWrite)) { stream.Seek(stream.Length, SeekOrigin.Begin); for (var i = 0; i < (searchDepth != -1 ? searchDepth : Int32.MaxValue); i++) { if (stream.Position == 0) { break; } byte[] headerBytes; int headerBytesLength; int endPosition; BubbleGroupDatabasePrimitives.ReadBubbleHeader(stream, out headerBytes, out headerBytesLength); BubbleGroupDatabasePrimitives.FindBubbleHeaderDelimiter(headerBytes, headerBytesLength, 0, out endPosition); var bubbleDataLength = BubbleGroupDatabasePrimitives.JumpBubbleData(stream); //we need to seek over the data. var guid = BubbleGroupDatabasePrimitives.ReadBubbleHeaderStruct(headerBytes, headerBytesLength, endPosition + 1, out endPosition); Tuple <string, VisualBubble> bubbleTuple; if ((bubbleTuple = bubbleTuples.FirstOrDefault(x => x.Item1 == guid)) == null) { continue; } var b = bubbleTuple.Item2; var bubbleSize = headerBytesLength + bubbleDataLength + 8; var cutStart = stream.Position + bubbleSize; var cutLength = stream.Length - cutStart; insertPosition = stream.Position; using (var ms = new MemoryStream()) { Serializer.Serialize(ms, b); var updatedBubbleData = ms.ToArray(); var updatedBubbleHeader = b.GetType().Name + ":" + b.ID + ":" + b.Time; var updatedBubbleSize = updatedBubbleHeader.Length + updatedBubbleData.Length + 8; var bubbleInjectDelta = bubbleSize - updatedBubbleSize; //enough room if (bubbleInjectDelta != 0) { var injectPosition = stream.Position; stream.Position = cutStart; //higher var cut = new byte[cutLength]; stream.Read(cut, 0, (int)cutLength); //should always work as long as the count ain't crazy high //var bw = new BinaryWriter(stream, Encoding.ASCII); stream.Position = injectPosition; BubbleGroupDatabasePrimitives.WriteBubbleData(stream, updatedBubbleData); BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, updatedBubbleHeader); stream.Write(cut, 0, cut.Length); stream.SetLength(stream.Position); } //they're the same! else if (bubbleInjectDelta == 0) { //var bw = new BinaryWriter(stream, Encoding.ASCII); BubbleGroupDatabasePrimitives.WriteBubbleData(stream, updatedBubbleData); BubbleGroupDatabasePrimitives.WriteBubbleHeader(stream, updatedBubbleHeader); } } if (cutLength == 0) { lastBubble = b; } bubbleTuples.Remove(bubbleTuple); if (!bubbleTuples.Any()) { break; } } BubbleGroupIndex.UpdateLastBubbleOrAndLastModifiedIndex(group.ID, lastBubble, insertPosition); } if (!bubbleTuples.Any()) { return(true); } return(false); } }
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); } }