void IssueList_UpdateItem(ListViewItem Item, IssueData Issue, DateTime Midnight) { Item.SubItems[IdHeader.Index].Text = Issue.Id.ToString(); Item.SubItems[CreatedHeader.Index].Text = FormatIssueDateTime(Issue.CreatedAt.ToLocalTime(), Midnight); Item.SubItems[ResolvedHeader.Index].Text = Issue.ResolvedAt.HasValue ? FormatIssueDateTime(Issue.ResolvedAt.Value.ToLocalTime(), Midnight) : "Unresolved"; Item.SubItems[TimeToFixHeader.Index].Text = Issue.ResolvedAt.HasValue ? Utility.FormatDurationMinutes(Issue.ResolvedAt.Value - Issue.CreatedAt) : "-"; Item.SubItems[OwnerHeader.Index].Text = (Issue.Owner == null) ? "-" : Utility.FormatUserName(Issue.Owner); Item.SubItems[DescriptionHeader.Index].Text = Issue.Summary; }
public IssueAlertWindow(IssueMonitor IssueMonitor, IssueData Issue, IssueAlertReason Reason) { this.IssueMonitor = IssueMonitor; this.Issue = Issue; InitializeComponent(); SetIssue(Issue, Reason); }
private void IssueListView_MouseDoubleClick(object sender, MouseEventArgs e) { ListViewHitTestInfo HitTest = IssueListView.HitTest(e.Location); if (HitTest.Item != null) { IssueData Issue = (IssueData)HitTest.Item.Tag; ShowIssue(Issue); } }
void IssueList_InsertItem(int ItemIdx, IssueData Issue, DateTime Midnight) { ListViewItem Item = new ListViewItem(""); for (int Idx = 0; Idx < IssueListView.Columns.Count - 1; Idx++) { Item.SubItems.Add(""); } Item.Tag = Issue; IssueList_UpdateItem(Item, Issue, Midnight); IssueListView.Items.Insert(ItemIdx, Item); }
void UpdateIssueList() { // Get the time at midnight DateTime Now = DateTime.Now; DateTime Midnight = (Now - Now.TimeOfDay).ToUniversalTime(); // Update the table int ItemIdx = 0; IssueListView.BeginUpdate(); foreach (IssueData Issue in Issues) { if (FilterProjectName == null || Issue.Project == FilterProjectName) { for (;;) { if (ItemIdx == IssueListView.Items.Count) { IssueList_InsertItem(ItemIdx, Issue, Midnight); break; } ListViewItem ExistingItem = IssueListView.Items[ItemIdx]; IssueData ExistingIssue = (IssueData)ExistingItem.Tag; if (ExistingIssue == null || ExistingIssue.Id < Issue.Id) { IssueList_InsertItem(ItemIdx, Issue, Midnight); break; } else if (ExistingIssue.Id == Issue.Id) { IssueList_UpdateItem(ExistingItem, Issue, Midnight); break; } else { IssueListView.Items.RemoveAt(ItemIdx); continue; } } ItemIdx++; } } while (ItemIdx < IssueListView.Items.Count) { IssueListView.Items.RemoveAt(ItemIdx); } IssueListView.EndUpdate(); // Update the maximum number of results StatusLabel.Text = (IssueListView.Items.Count == Issues.Count)? String.Format("Showing {0} results matching project '{1}'.", Issues.Count, FilterProjectName) : String.Format("Showing {0}/{1} results matching project '{2}'.", IssueListView.Items.Count, Issues.Count, FilterProjectName); }
private void ShowIssue(IssueData Issue) { try { Issue.Builds = RESTApi.GET <List <IssueBuildData> >(IssueMonitor.ApiUrl, String.Format("issues/{0}/builds", Issue.Id)); } catch (Exception Ex) { MessageBox.Show(Owner, Ex.ToString(), "Error querying builds", MessageBoxButtons.OK); return; } IssueDetailsWindow.Show(Owner, IssueMonitor, ServerAndPort, UserName, ServerTimeOffset, Issue, Log, CurrentStream); }
private void IssueListView_DrawSubItem(object sender, DrawListViewSubItemEventArgs e) { IssueData Issue = (IssueData)e.Item.Tag; if (e.ColumnIndex == IconHeader.Index) { if (!Issue.ResolvedAt.HasValue && Issue.FixChange == 0) { IssueListView.DrawIcon(e.Graphics, e.Bounds, WorkspaceControl.BadBuildIcon); } else { IssueListView.DrawIcon(e.Graphics, e.Bounds, WorkspaceControl.GoodBuildIcon); } } else { IssueListView.DrawNormalSubItem(e); } }
static bool ApplyPendingUpdate(List <IssueData> Issues, IssueUpdateData Update) { bool bUpdated = false; for (int Idx = 0; Idx < Issues.Count; Idx++) { IssueData Issue = Issues[Idx]; if (Update.Id == Issue.Id) { if (Update.Owner != null && Update.Owner != Issue.Owner) { Issue.Owner = Update.Owner; bUpdated = true; } if (Update.NominatedBy != null && Update.NominatedBy != Issue.NominatedBy) { Issue.NominatedBy = Update.NominatedBy; bUpdated = true; } if (Update.Acknowledged.HasValue && Update.Acknowledged.Value != Issue.AcknowledgedAt.HasValue) { Issue.AcknowledgedAt = Update.Acknowledged.Value? (DateTime?)DateTime.UtcNow : null; bUpdated = true; } if (Update.FixChange.HasValue) { Issue.FixChange = Update.FixChange.Value; if (Issue.FixChange != 0) { Issues.RemoveAt(Idx); } bUpdated = true; } break; } } return(bUpdated); }
private void IssueListView_DrawItem(object sender, DrawListViewItemEventArgs e) { if (e.Item.Selected) { IssueListView.DrawSelectedBackground(e.Graphics, e.Bounds); } else if (e.ItemIndex == IssueListView.HoverItem) { IssueListView.DrawTrackedBackground(e.Graphics, e.Bounds); } else if (e.Item.Tag is IssueData) { IssueData Issue = (IssueData)e.Item.Tag; Color BackgroundColor; if (Issue.ResolvedAt.HasValue) { BackgroundColor = SystemColors.Window; //Color.FromArgb(248, 254, 246); } else if (Issue.FixChange > 0) { BackgroundColor = Color.FromArgb(245, 245, 245); } else { BackgroundColor = Color.FromArgb(254, 248, 246); } using (SolidBrush Brush = new SolidBrush(BackgroundColor)) { e.Graphics.FillRectangle(Brush, e.Bounds); } } else { IssueListView.DrawDefaultBackground(e.Graphics, e.Bounds); } }
bool ReadCurrentIssues() { try { Stopwatch Timer = Stopwatch.StartNew(); LogWriter.WriteLine(); LogWriter.WriteLine("Polling for notifications at {0}...", DateTime.Now.ToString()); // Get the initial number of issues. We won't post updates if this stays at zero. int InitialNumIssues = Issues.Count; // Fetch the new issues List <IssueData> NewIssues = RESTApi.GET <List <IssueData> >(ApiUrl, String.Format("issues?user={0}", UserName)); // Check if we're tracking a particular issue. If so, we want updates even when it's resolved. long[] LocalTrackingIssueIds; lock (LockObject) { LocalTrackingIssueIds = TrackingIssueIds.Distinct().ToArray(); } foreach (long LocalTrackingIssueId in LocalTrackingIssueIds) { if (!NewIssues.Any(x => x.Id == LocalTrackingIssueId)) { try { IssueData Issue = RESTApi.GET <IssueData>(ApiUrl, String.Format("issues/{0}", LocalTrackingIssueId)); if (Issue != null) { NewIssues.Add(Issue); } } catch (Exception Ex) { LogWriter.WriteException(Ex, "Exception while fetching tracked issue"); } } } // Update all the builds for each issue foreach (IssueData NewIssue in NewIssues) { NewIssue.Builds = RESTApi.GET <List <IssueBuildData> >(ApiUrl, String.Format("issues/{0}/builds", NewIssue.Id)); } // Apply any pending updates to this issue list, and update it lock (LockObject) { foreach (IssueUpdateData PendingUpdate in PendingUpdates) { ApplyPendingUpdate(NewIssues, PendingUpdate); } Issues = NewIssues; } // Update the main thread if (InitialNumIssues > 0 || Issues.Count > 0) { if (OnIssuesChanged != null) { OnIssuesChanged(); } } // Update the stats LastStatusMessage = String.Format("Last update took {0}ms", Timer.ElapsedMilliseconds); LogWriter.WriteLine("Done in {0}ms.", Timer.ElapsedMilliseconds); return(true); } catch (Exception Ex) { LogWriter.WriteException(Ex, "Failed with exception."); LastStatusMessage = String.Format("Last update failed: ({0})", Ex.ToString()); return(false); } }
public void SetIssue(IssueData NewIssue, IssueAlertReason NewReason) { bool bNewStrongAlert = false; StringBuilder OwnerTextBuilder = new StringBuilder(); if (NewIssue.Owner == null) { OwnerTextBuilder.Append("Currently unassigned."); } else { if (String.Compare(NewIssue.Owner, IssueMonitor.UserName, StringComparison.OrdinalIgnoreCase) == 0) { if (NewIssue.NominatedBy != null) { OwnerTextBuilder.AppendFormat("You have been nominated to fix this issue by {0}.", Utility.FormatUserName(NewIssue.NominatedBy)); } else { OwnerTextBuilder.AppendFormat("Assigned to {0}.", Utility.FormatUserName(NewIssue.Owner)); } bNewStrongAlert = true; } else { OwnerTextBuilder.AppendFormat("Assigned to {0}", Utility.FormatUserName(NewIssue.Owner)); if (NewIssue.NominatedBy != null) { OwnerTextBuilder.AppendFormat(" by {0}", Utility.FormatUserName(NewIssue.NominatedBy)); } if (!NewIssue.AcknowledgedAt.HasValue && (NewReason & IssueAlertReason.UnacknowledgedTimer) != 0) { OwnerTextBuilder.Append(" (not acknowledged)"); } OwnerTextBuilder.Append("."); } } OwnerTextBuilder.AppendFormat(" Open for {0}.", Utility.FormatDurationMinutes((int)(NewIssue.RetrievedAt - NewIssue.CreatedAt).TotalMinutes)); string OwnerText = OwnerTextBuilder.ToString(); bool bNewIsWarning = Issue.Builds.Count > 0 && !Issue.Builds.Any(x => x.Outcome != IssueBuildOutcome.Warning); Issue = NewIssue; string Summary = NewIssue.Summary; int MaxLength = 128; if (Summary.Length > MaxLength) { Summary = Summary.Substring(0, MaxLength).TrimEnd() + "..."; } if (Summary != SummaryLabel.Text || OwnerText != OwnerLabel.Text || Reason != NewReason || bIsWarning != bNewIsWarning || bStrongAlert != bNewStrongAlert) { Rectangle PrevBounds = Bounds; SuspendLayout(); SummaryLabel.Text = Summary; OwnerLabel.Text = OwnerText; bool bForceUpdateButtons = false; if (bStrongAlert != bNewStrongAlert) { bStrongAlert = bNewStrongAlert; if (bNewStrongAlert) { SummaryLabel.ForeColor = Color.FromArgb(255, 255, 255); SummaryLabel.LinkColor = Color.FromArgb(255, 255, 255); OwnerLabel.ForeColor = Color.FromArgb(255, 255, 255); DetailsBtn.Theme = AlertButtonControl.AlertButtonTheme.Strong; AcceptBtn.Theme = AlertButtonControl.AlertButtonTheme.Strong; LatestBuildLinkLabel.LinkColor = Color.FromArgb(255, 255, 255); } else { SummaryLabel.ForeColor = Color.FromArgb(32, 32, 64); SummaryLabel.LinkColor = Color.FromArgb(32, 32, 64); OwnerLabel.ForeColor = Color.FromArgb(32, 32, 64); DetailsBtn.Theme = AlertButtonControl.AlertButtonTheme.Normal; AcceptBtn.Theme = AlertButtonControl.AlertButtonTheme.Green; LatestBuildLinkLabel.LinkColor = Color.FromArgb(16, 102, 192); } bForceUpdateButtons = true; } if (bIsWarning != bNewIsWarning) { bIsWarning = bNewIsWarning; } if (Reason != NewReason || bForceUpdateButtons) { Reason = NewReason; List <Button> Buttons = new List <Button>(); Buttons.Add(DetailsBtn); if ((NewReason & IssueAlertReason.Owner) != 0) { AcceptBtn.Text = "Acknowledge"; Buttons.Add(AcceptBtn); } else if ((NewReason & IssueAlertReason.Normal) != 0) { AcceptBtn.Text = "Will Fix"; Buttons.Add(AcceptBtn); DeclineBtn.Text = "Not Me"; Buttons.Add(DeclineBtn); } else { DeclineBtn.Text = "Dismiss"; Buttons.Add(DeclineBtn); } tableLayoutPanel3.ColumnCount = Buttons.Count; tableLayoutPanel3.Controls.Clear(); for (int Idx = 0; Idx < Buttons.Count; Idx++) { tableLayoutPanel3.Controls.Add(Buttons[Idx], Idx, 0); } } ResumeLayout(true); Location = new Point(PrevBounds.Right - Bounds.Width, PrevBounds.Y); Invalidate(); } }
private void ShowIssue(IssueData Issue) { IssueDetailsWindow.Show(Owner, IssueMonitor, ServerAndPort, UserName, ServerTimeOffset, Issue, Log, CurrentStream); }
void UpdateIssueList() { // Get the time at midnight DateTime Now = DateTime.Now; DateTime Midnight = (Now - Now.TimeOfDay).ToUniversalTime(); // Get the regex for the selected filter Func <IssueData, bool> Filter; if (String.IsNullOrEmpty(FilterName)) { Filter = x => true; } else if (!CustomFilters.TryGetValue(FilterName, out Filter)) { Filter = x => x.Streams == null || x.Streams.Any(y => String.Equals(y, FilterName, StringComparison.OrdinalIgnoreCase)); } // Update the table int ItemIdx = 0; IssueListView.BeginUpdate(); foreach (IssueData Issue in Issues) { if (Filter(Issue)) { for (;;) { if (ItemIdx == IssueListView.Items.Count) { IssueList_InsertItem(ItemIdx, Issue, Midnight); break; } ListViewItem ExistingItem = IssueListView.Items[ItemIdx]; IssueData ExistingIssue = (IssueData)ExistingItem.Tag; if (ExistingIssue == null || ExistingIssue.Id < Issue.Id) { IssueList_InsertItem(ItemIdx, Issue, Midnight); break; } else if (ExistingIssue.Id == Issue.Id) { IssueList_UpdateItem(ExistingItem, Issue, Midnight); break; } else { IssueListView.Items.RemoveAt(ItemIdx); continue; } } ItemIdx++; } } while (ItemIdx < IssueListView.Items.Count) { IssueListView.Items.RemoveAt(ItemIdx); } IssueListView.EndUpdate(); // Update the maximum number of results string FilterText = ""; if (!String.IsNullOrEmpty(FilterName)) { FilterText = String.Format(" matching filter '{0}'", FilterName); } StatusLabel.Text = (IssueListView.Items.Count == Issues.Count)? String.Format("Showing {0} results{1}.", Issues.Count, FilterText) : String.Format("Showing {0}/{1} results{2}.", IssueListView.Items.Count, Issues.Count, FilterText); }
private void ShowIssue(IssueData Issue) { Issue.Builds = RESTApi.GET <List <IssueBuildData> >(IssueMonitor.ApiUrl, String.Format("issues/{0}/builds", Issue.Id)); IssueDetailsWindow.Show(Owner, IssueMonitor, ServerAndPort, UserName, ServerTimeOffset, Issue, Log, CurrentStream); }