NotifyMeCI.GUI/Form1.cs
/*
Notify Me CI is designed to be an awesome CI desktop notifier.
Copyright (c) 2016, Matthew Kelly (Badgerati)
Company: Cadaeic Studios
License: MIT (see LICENSE for details)
*/
using NotifyMeCI.Engine.Enums;
using NotifyMeCI.Engine.Events;
using NotifyMeCI.Engine.Repositories.Interfaces;
using NotifyMeCI.Engine.Objects;
using NotifyMeCI.Injector;
using System;
using System.Collections.Generic;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;
using System.Threading.Tasks;
using NotifyMeCI.GUI.Properties;
using NotifyMeCI.Engine.Tasks;
using System.Diagnostics;
using NotifyMeCI.Engine.Managers;
using NotifyMeCI.Engine;
namespace NotifyMeCI.GUI
{
public partial class Form1 : Form
{
#region Repositories
private ICIServerRepository CIServerRepository
{
get { return DIContainer.Instance.Get<ICIServerRepository>(); }
}
#endregion
#region Fields
private IList<CIServer> Servers = new List<CIServer>(2);
private IList<CIJob> Jobs = new List<CIJob>(10);
private JobTask JobTask = default(JobTask);
private NotifyTask NotifyTask = default(NotifyTask);
private ConsoleOptions Options = default(ConsoleOptions);
#endregion
#region Constructor
public Form1(ConsoleOptions options)
{
// initialise
Options = options;
InitializeComponent();
// events
Shown += Form1_Shown;
FormClosing += Form1_FormClosing;
FormClosed += Form1_FormClosed;
Resize += Form1_Resize;
ApplicationEvents.Instance.Start();
// setup lists
SetupServerList();
// show server tab if no servers setup
if (Servers == default(IList<CIServer>) || !Servers.Any())
{
tabControl1.SelectedTab = ServersTab;
}
tabControl1.SelectedIndexChanged += TabControl1_SelectedIndexChanged;
// setup notify event
TaskbarNotifier.BalloonTipClicked += TaskbarNotifier_BalloonTipClicked;
TaskbarNotifier.MouseDoubleClick += TaskbarNotifier_MouseDoubleClick;
// start parallel task
JobTask = new JobTask(UpdateJobsList);
NotifyTask = new NotifyTask(NotifyJob);
Task.Run(() => Parallel.ForEach(new ITask[] { JobTask, NotifyTask }, task => task.Run()));
}
#endregion
#region Events
private void Form1_Shown(object sender, EventArgs e)
{
if ((Options != default(ConsoleOptions) && Options.Minimize) || SettingManager.Instance.Minimize)
{
WindowState = FormWindowState.Minimized;
Hide();
TaskbarNotifier.Visible = true;
}
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
CloseForm();
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
CloseForm();
}
private void TabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
var tab = tabControl1.SelectedTab;
if (sender == default(object) || tab == default(TabPage))
{
return;
}
if (tab.Text.Equals("Servers", StringComparison.InvariantCultureIgnoreCase))
{
SetupServerList();
}
}
private void Form1_Resize(object sender, EventArgs e)
{
switch (WindowState)
{
case FormWindowState.Minimized:
TaskbarNotifier.Visible = true;
Hide();
break;
case FormWindowState.Normal:
if (JobTask != default(ITask) && !JobTask.IsInterrupted)
{
JobTask.CoreLogic();
}
UpdateJobGui(Jobs);
break;
}
}
private void TaskbarNotifier_MouseDoubleClick(object sender, MouseEventArgs e)
{
Show();
WindowState = FormWindowState.Normal;
}
private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
{
MessageBox.Show("Author: Matthew Kelly\nVersion: " + Logger.Version,
"Notify Me CI",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
private void ServerTypeDdl_SelectedIndexChanged(object sender, EventArgs e)
{
var serverType = default(CIServerType);
var selectedItem = ServerTypeDdl.SelectedItem;
var serverTypeTxt = selectedItem == default(object)
? string.Empty
: selectedItem.ToString();
// toggle the API token control
if (!Enum.TryParse<CIServerType>(serverTypeTxt, true, out serverType))
{
ApiTokenTxt.Visible = true;
ApiTokenLbl.Visible = true;
return;
}
switch (serverType)
{
case CIServerType.AppVeyor:
ServerUrlTxt.Text = "https://ci.appveyor.com/api/projects";
break;
case CIServerType.TravisCI:
ServerUrlTxt.Text = "https://api.travis-ci.org/repos/<USERNAME>";
break;
}
switch (serverType)
{
case CIServerType.TravisCI:
ApiTokenTxt.Visible = false;
ApiTokenLbl.Visible = false;
break;
default:
ApiTokenTxt.Visible = true;
ApiTokenLbl.Visible = true;
break;
}
}
private void AddServerBtn_Click(object sender, EventArgs e)
{
var errorTitle = "Error Adding New Server";
try
{
// validate server info
var selectedItem = ServerTypeDdl.SelectedItem;
var serverTypeTxt = selectedItem == default(object)
? string.Empty
: selectedItem.ToString();
var error = Validator.ValidateServerValues(serverTypeTxt, ServerNameTxt.Text, ServerUrlTxt.Text, (int)PollIntervalNbr.Value, ApiTokenTxt.Text);
if (!string.IsNullOrWhiteSpace(error))
{
Logger.ShowErrorMessage(error, errorTitle);
return;
}
// get server type
var serverType = default(CIServerType);
Enum.TryParse<CIServerType>(serverTypeTxt, true, out serverType);
// insert server
var server = new CIServer()
{
ServerType = serverType,
Name = ServerNameTxt.Text,
Url = ServerUrlTxt.Text,
PollInterval = (int)PollIntervalNbr.Value,
CurrentlyPolling = false,
Enabled = true,
ApiToken = ApiTokenTxt.Visible ? ApiTokenTxt.Text : string.Empty
};
CIServerRepository.Insert(server);
// clear fields
ApiTokenTxt.Text = string.Empty;
ServerUrlTxt.Text = string.Empty;
ServerNameTxt.Text = string.Empty;
PollIntervalNbr.Value = 30;
// declare success
MessageBox.Show("New server added successfully", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
// refresh server and job lists
SetupServerList();
}
catch (Exception ex)
{
Logger.ShowExceptionMessage("There was a fatal issue adding a new server:", ex, errorTitle);
return;
}
}
private void ServerListView_DoubleClick(object sender, EventArgs e)
{
if (ServerListView.SelectedItems == null || ServerListView.SelectedItems.Count == 0)
{
return;
}
var server = Servers[ServerListView.SelectedIndices[0]];
var edit = new EditServerForm(server);
var result = edit.ShowDialog();
if (result == DialogResult.OK || result == DialogResult.None)
{
SetupServerList();
}
}
private void settingsToolStripMenuItem_Click(object sender, EventArgs e)
{
var settings = new SettingsForm();
settings.ShowDialog();
UpdateJobGui(Jobs);
}
private void JobListView_DoubleClick(object sender, EventArgs e)
{
if (JobListView.SelectedItems == null || JobListView.SelectedItems.Count == 0)
{
return;
}
var job = Jobs[JobListView.SelectedIndices[0]];
Process.Start(job.Url);
}
private void TaskbarNotifier_BalloonTipClicked(object sender, EventArgs e)
{
if (sender == default(object) || NotifyTask.CurrentNotifyJob == default(CIJob))
{
return;
}
Process.Start(NotifyTask.CurrentNotifyJob.Url);
}
#endregion
#region Private Helpers
private void SetupServerList()
{
// get all servers
Servers = CIServerRepository.All();
if (Servers == default(IList<CIServer>) || Servers.Count == 0)
{
return;
}
ServerListView.Items.Clear();
ServerListView.SmallImageList = new ImageList() { ImageSize = new Size(1, 26) };
foreach (var server in Servers)
{
var item = new ListViewItem(new string[]
{
server.Name,
server.ServerType.ToString(),
server.Url,
TimeSpan.FromSeconds(server.PollInterval).ToString("h'h 'm'm 's's'"),
server.LastPollDate.ToLongDateString() + " " + server.LastPollDate.ToLongTimeString(),
server.Enabled.ToString()
});
item.BackColor = server.Enabled ? Preferences.SUCCESS_COLOR : Preferences.FAIL_COLOR;
item.ForeColor = Color.Black;
ServerListView.Items.Add(item);
}
}
private void UpdateJobsList(IList<CIJob> jobs)
{
if (jobs == default(IList<CIJob>) || !jobs.Any())
{
return;
}
var informList = new List<CIJob>(10);
// compare these jobs to the current ones
foreach (var job in jobs)
{
// fetch for the job
var _job = Jobs.SingleOrDefault(x => x.Name == job.Name && x.ServerName == job.ServerName);
// if the jobs exists with the same status then continue
if (_job != default(CIJob) && _job.BuildStatus == job.BuildStatus)
{
UpdateJob(_job, job);
continue;
}
// if the jobs doesn't exist add it
if (_job == default(CIJob))
{
Jobs.Add(job);
// if it's failing or building inform about it
if (job.BuildStatus == BuildStatusType.Failed || job.BuildStatus == BuildStatusType.Building)
{
informList.Add(job);
}
}
// else, the job exists, but it's status has changed
else
{
// determine if job needs to be informed
if (_job.BuildStatus != job.BuildStatus)
{
informList.Add(job);
}
// update the job
UpdateJob(_job, job);
}
}
// remove jobs from the list that no longer exist on the server
var updatedServers = jobs.Select(j => j.ServerName).Distinct();
var deadJobs = Jobs.Where(x => !jobs.Any(y => y.Name == x.Name && y.ServerName == x.ServerName));
deadJobs = deadJobs.Where(d => updatedServers.Contains(d.ServerName));
foreach (var job in deadJobs.ToList())
{
Jobs.Remove(job);
}
// sort the jobs
Jobs = Jobs.OrderBy(a => a.BuildStatus, new BuildStatusTypeComparer()).ToList();
// show the inform list
UpdateNotifyIcon(Jobs);
NotifyOfJobs(informList);
// if the GUI is open, update the ListView
if (WindowState == FormWindowState.Normal || WindowState == FormWindowState.Maximized)
{
UpdateJobGui(Jobs);
}
}
private void UpdateNotifyIcon(IList<CIJob> jobs)
{
if (jobs == default(IList<CIJob>) || !jobs.Any())
{
return;
}
if (jobs.Any(x => (x.BuildStatus == BuildStatusType.Aborted && SettingManager.Instance.AbortedEqualsFailed) || x.BuildStatus == BuildStatusType.Failed))
{
TaskbarNotifier.Icon = Resources.red_icon;
}
else if (jobs.Any(x => x.BuildStatus == BuildStatusType.Building))
{
TaskbarNotifier.Icon = Resources.yellow_icon;
}
else
{
TaskbarNotifier.Icon = Resources.green_icon;
}
}
private void NotifyOfJobs(IList<CIJob> jobs)
{
jobs.ToList().ForEach(x => NotifyTask.NotifyQueue.Enqueue(x));
}
private void NotifyJob(CIJob job, int sleep)
{
TaskbarNotifier.ShowBalloonTip(sleep, job.Name, string.Format("{0} on {1}", job.BuildStatus, job.ServerName), ToolTipIcon.Info);
}
private void UpdateJobGui(IList<CIJob> jobs)
{
// Clear the current jobs list
if (JobListView.InvokeRequired)
{
JobListView.Invoke((MethodInvoker)delegate { JobListView.Items.Clear(); });
}
else
{
JobListView.Items.Clear();
}
// Set the list view height
if (JobListView.InvokeRequired)
{
JobListView.Invoke((MethodInvoker)delegate { JobListView.SmallImageList = new ImageList() { ImageSize = new Size(1, 26) }; });
}
else
{
JobListView.SmallImageList = new ImageList() { ImageSize = new Size(1, 26) };
}
// Setup each job in the list
foreach (var job in jobs)
{
if (!SettingManager.Instance.BuildStatuses.IsVisible(job.BuildStatus))
{
continue;
}
var item = new ListViewItem(new string[]
{
job.BuildId.ToString(),
job.Name,
job.ServerName,
job.TimeStamp.ToLongDateString() + " " + job.TimeStamp.ToLongTimeString(),
TimeSpan.FromSeconds(job.Duration).ToString("h'h 'm'm 's's'"),
job.BuildStatus.ToString()
});
item.BackColor = SettingManager.Instance.BuildStatuses.MapColor(job.BuildStatus);
item.ForeColor = Color.Black;
if (JobListView.InvokeRequired)
{
JobListView.Invoke((MethodInvoker)delegate { JobListView.Items.Add(item); });
}
else
{
JobListView.Items.Add(item);
}
}
}
private void UpdateJob(CIJob jobToUpdate, CIJob job)
{
jobToUpdate.BuildId = job.BuildId;
jobToUpdate.BuildStatus = job.BuildStatus;
jobToUpdate.Duration = job.Duration;
jobToUpdate.TimeStamp = job.TimeStamp;
jobToUpdate.Url = job.Url;
}
private void CloseForm()
{
JobTask.Interrupt();
NotifyTask.Interrupt();
if (TaskbarNotifier != default(NotifyIcon))
{
TaskbarNotifier.Visible = false;
TaskbarNotifier.Icon = null;
TaskbarNotifier.Dispose();
}
}
#endregion
}
}