Adds the github login prompt

This commit is contained in:
Cyberboss
2017-11-11 19:06:37 -05:00
parent 42a429c3c0
commit ea92cc09b4
8 changed files with 515 additions and 58 deletions
+6
View File
@@ -44,6 +44,12 @@
<setting name="RemoteEntropy" serializeAs="String">
<value />
</setting>
<setting name="GitHubAPIKey" serializeAs="String">
<value />
</setting>
<setting name="GitHubAPIKeyEntropy" serializeAs="String">
<value />
</setting>
</TGControlPanel.Properties.Settings>
<TGControlPanel.Properties.Settings1>
<setting name="LastPageIndex" serializeAs="String">
+176
View File
@@ -0,0 +1,176 @@
namespace TGControlPanel
{
partial class GitHubLoginPrompt
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.PasswordLabel = new System.Windows.Forms.Label();
this.UsernameLabel = new System.Windows.Forms.Label();
this.PasswordTextBox = new System.Windows.Forms.TextBox();
this.UsernameTextBox = new System.Windows.Forms.TextBox();
this.DividerLabel = new System.Windows.Forms.Label();
this.APIKeyLabel = new System.Windows.Forms.Label();
this.APIKeyTextBox = new System.Windows.Forms.TextBox();
this.OrLabel = new System.Windows.Forms.Label();
this.LoginButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// PasswordLabel
//
this.PasswordLabel.AutoSize = true;
this.PasswordLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.PasswordLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.PasswordLabel.Location = new System.Drawing.Point(12, 41);
this.PasswordLabel.Name = "PasswordLabel";
this.PasswordLabel.Size = new System.Drawing.Size(92, 18);
this.PasswordLabel.TabIndex = 25;
this.PasswordLabel.Text = "Password:";
this.PasswordLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// UsernameLabel
//
this.UsernameLabel.AutoSize = true;
this.UsernameLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.UsernameLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.UsernameLabel.Location = new System.Drawing.Point(12, 9);
this.UsernameLabel.Name = "UsernameLabel";
this.UsernameLabel.Size = new System.Drawing.Size(97, 18);
this.UsernameLabel.TabIndex = 24;
this.UsernameLabel.Text = "Username:";
this.UsernameLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// PasswordTextBox
//
this.PasswordTextBox.Location = new System.Drawing.Point(132, 41);
this.PasswordTextBox.Name = "PasswordTextBox";
this.PasswordTextBox.Size = new System.Drawing.Size(233, 20);
this.PasswordTextBox.TabIndex = 23;
this.PasswordTextBox.UseSystemPasswordChar = true;
//
// UsernameTextBox
//
this.UsernameTextBox.Location = new System.Drawing.Point(132, 9);
this.UsernameTextBox.Name = "UsernameTextBox";
this.UsernameTextBox.Size = new System.Drawing.Size(233, 20);
this.UsernameTextBox.TabIndex = 22;
//
// DividerLabel
//
this.DividerLabel.AutoSize = true;
this.DividerLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.DividerLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.DividerLabel.Location = new System.Drawing.Point(-24, 59);
this.DividerLabel.Name = "DividerLabel";
this.DividerLabel.Size = new System.Drawing.Size(468, 18);
this.DividerLabel.TabIndex = 26;
this.DividerLabel.Text = "______________________________________________";
this.DividerLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// APIKeyLabel
//
this.APIKeyLabel.AutoSize = true;
this.APIKeyLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.APIKeyLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.APIKeyLabel.Location = new System.Drawing.Point(12, 95);
this.APIKeyLabel.Name = "APIKeyLabel";
this.APIKeyLabel.Size = new System.Drawing.Size(78, 18);
this.APIKeyLabel.TabIndex = 28;
this.APIKeyLabel.Text = "API Key:";
this.APIKeyLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// APIKeyTextBox
//
this.APIKeyTextBox.Location = new System.Drawing.Point(132, 95);
this.APIKeyTextBox.Name = "APIKeyTextBox";
this.APIKeyTextBox.Size = new System.Drawing.Size(233, 20);
this.APIKeyTextBox.TabIndex = 27;
this.APIKeyTextBox.UseSystemPasswordChar = true;
//
// OrLabel
//
this.OrLabel.AutoSize = true;
this.OrLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.OrLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.OrLabel.Location = new System.Drawing.Point(157, 64);
this.OrLabel.Name = "OrLabel";
this.OrLabel.Size = new System.Drawing.Size(32, 18);
this.OrLabel.TabIndex = 29;
this.OrLabel.Text = "OR";
this.OrLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// LoginButton
//
this.LoginButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.LoginButton.Location = new System.Drawing.Point(145, 133);
this.LoginButton.Name = "LoginButton";
this.LoginButton.Size = new System.Drawing.Size(88, 25);
this.LoginButton.TabIndex = 30;
this.LoginButton.Text = InitalLoginButtonText;
this.LoginButton.UseVisualStyleBackColor = true;
this.LoginButton.Click += new System.EventHandler(this.LoginButton_Click);
//
// GitHubLoginPrompt
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34)))));
this.ClientSize = new System.Drawing.Size(376, 170);
this.Controls.Add(this.LoginButton);
this.Controls.Add(this.OrLabel);
this.Controls.Add(this.APIKeyLabel);
this.Controls.Add(this.APIKeyTextBox);
this.Controls.Add(this.DividerLabel);
this.Controls.Add(this.PasswordLabel);
this.Controls.Add(this.UsernameLabel);
this.Controls.Add(this.PasswordTextBox);
this.Controls.Add(this.UsernameTextBox);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "GitHubLoginPrompt";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Login To GitHub";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label PasswordLabel;
private System.Windows.Forms.Label UsernameLabel;
private System.Windows.Forms.TextBox PasswordTextBox;
private System.Windows.Forms.TextBox UsernameTextBox;
private System.Windows.Forms.Label DividerLabel;
private System.Windows.Forms.Label APIKeyLabel;
private System.Windows.Forms.TextBox APIKeyTextBox;
private System.Windows.Forms.Label OrLabel;
private System.Windows.Forms.Button LoginButton;
}
}
+98
View File
@@ -0,0 +1,98 @@
using Octokit;
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using TGServiceInterface;
namespace TGControlPanel
{
/// <summary>
/// Used for recieving a GitHub API key for use in <see cref="Credentials"/>
/// </summary>
sealed partial class GitHubLoginPrompt : Form
{
/// <summary>
/// The text to appear on <see cref="LoginButton"/>
/// </summary>
const string InitalLoginButtonText = "Login";
/// <summary>
/// The <see cref="GitHubClient"/> to use for OAuth requests
/// </summary>
readonly GitHubClient client;
/// <summary>
/// Construct a <see cref="GitHubLoginPrompt"/>
/// </summary>
/// <param name="c">The <see cref="GitHubClient"/> to use for requests</param>
public GitHubLoginPrompt(GitHubClient c)
{
InitializeComponent();
AcceptButton = LoginButton;
DialogResult = DialogResult.Cancel;
client = c;
}
/// <summary>
/// Calls <see cref="GetAPIKey"/>. On success, encrypts it's return value, saves it in <see cref="Properties.Settings.GitHubAPIKey"/> and <see cref="Properties.Settings.GitHubAPIKeyEntropy"/>, and closes the <see cref="GitHubLoginPrompt"/>. On failure, shows a <see cref="MessageBox"/> and exits
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
async void LoginButton_Click(object sender, EventArgs e)
{
LoginButton.Text = "Logging in...";
Enabled = false;
var APIKey = await GetAPIKey();
if(APIKey == null)
{
MessageBox.Show("Authentication failure!");
Enabled = true;
LoginButton.Text = "Login";
return;
}
//Encrypt it and let's be on our way
var Config = Properties.Settings.Default;
Config.GitHubAPIKey = Helpers.EncryptData(APIKey, out string entropy);
Config.GitHubAPIKeyEntropy = entropy;
DialogResult = DialogResult.OK;
Close();
}
/// <summary>
/// Retrieves an API key from GitHub based on login information or from the <see cref="APIKeyTextBox"/> and verifies it
/// </summary>
/// <returns>The GitHub API key on success, <see langword="null"/> on failure</returns>
async Task<string> GetAPIKey()
{
string APIKey;
if (String.IsNullOrWhiteSpace(APIKeyTextBox.Text))
{
try
{
client.Credentials = new Credentials(UsernameTextBox.Text, PasswordTextBox.Text);
var token = await client.Authorization.Create(new NewAuthorization { Note = "TGControlPanel token to bypass rate limiting" });
APIKey = token.Token;
}
catch (AuthorizationException)
{
return null;
}
}
else
APIKey = APIKeyTextBox.Text;
//validate it by pinging a random repository
client.Credentials = new Credentials(APIKey);
try
{
await client.Repository.Get("Dextraspace", "Test");
}
catch (AuthorizationException)
{
return null;
}
return APIKey;
}
}
}
+120
View File
@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
+25 -1
View File
@@ -12,7 +12,7 @@ namespace TGControlPanel.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.1.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
@@ -142,5 +142,29 @@ namespace TGControlPanel.Properties {
this["RemoteEntropy"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string GitHubAPIKey {
get {
return ((string)(this["GitHubAPIKey"]));
}
set {
this["GitHubAPIKey"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string GitHubAPIKeyEntropy {
get {
return ((string)(this["GitHubAPIKeyEntropy"]));
}
set {
this["GitHubAPIKeyEntropy"] = value;
}
}
}
}
@@ -32,5 +32,11 @@
<Setting Name="RemoteEntropy" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="GitHubAPIKey" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="GitHubAPIKeyEntropy" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>
+9
View File
@@ -64,6 +64,12 @@
<Compile Include="CountedForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="GitHubLoginPrompt.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="GitHubLoginPrompt.Designer.cs">
<DependentUpon>GitHubLoginPrompt.cs</DependentUpon>
</Compile>
<Compile Include="InstanceSelector.cs">
<SubType>Form</SubType>
</Compile>
@@ -108,6 +114,9 @@
<Compile Include="ServerOpForm.cs">
<SubType>Form</SubType>
</Compile>
<EmbeddedResource Include="GithubLoginPrompt.resx">
<DependentUpon>GitHubLoginPrompt.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="InstanceSelector.resx">
<DependentUpon>InstanceSelector.cs</DependentUpon>
</EmbeddedResource>
+75 -57
View File
@@ -30,78 +30,96 @@ namespace TGControlPanel
}
/// <summary>
/// Populate <see cref="PullRequestListBox"/> with <see cref="PullRequestInfo"/> from github and check off ones that are currently merged
/// Populate <see cref="PullRequestListBox"/> with <see cref="PullRequestInfo"/> from github and check off ones that are currently merged. Prompts the user to login to GitHub if they hit the rate limit
/// </summary>
async void LoadPullRequests()
/// <param name="client">The <see cref="GitHubClient"/> to use. If <see langword="null"/>, one will be created</param>
async void LoadPullRequests(GitHubClient client)
{
Enabled = false;
UseWaitCursor = true;
if(client == null)
client = new GitHubClient(new ProductHeaderValue(Assembly.GetExecutingAssembly().GetName().Name));
var config = Properties.Settings.Default;
if (!String.IsNullOrWhiteSpace(config.GitHubAPIKey))
client.Credentials = new Credentials(Helpers.DecryptData(config.GitHubAPIKey, config.GitHubAPIKeyEntropy));
try
{
PullRequestListBox.Items.Clear();
var repo = currentInterface.GetComponent<ITGRepository>();
string remote = null, error = null, error2 = null;
IList<PullRequestInfo> pulls = null;
var remoteRequest = WrapServerOp(() => remote = repo.GetRemote(out error));
//get started on this while we're processing here
var pullsRequest = Task.Factory.StartNew(() => pulls = repo.MergedPullRequests(out error2));
await remoteRequest;
if (remote == null)
{
MessageBox.Show(String.Format("Error retrieving remote repository: {0}", error));
return;
}
if (!remote.Contains("github.com"))
{
MessageBox.Show("Pull request support is only available for github based repositories!", "Error");
return;
}
//Assume standard gh format: [(git)|(https)]://github.com/owner/repo(.git)[0-1]
var splits = remote.Split('/');
var repoName = splits[splits.Length - 1];
var repoOwner = splits[splits.Length - 2];
//Search for open PRs
var client = new GitHubClient(new ProductHeaderValue(Assembly.GetExecutingAssembly().GetName().Name));
Enabled = false;
UseWaitCursor = true;
SearchIssuesResult result;
try
{
result = await client.Search.SearchIssues(new SearchIssuesRequest
PullRequestListBox.Items.Clear();
var repo = currentInterface.GetComponent<ITGRepository>();
string remote = null, error = null, error2 = null;
IList<PullRequestInfo> pulls = null;
var remoteRequest = WrapServerOp(() => remote = repo.GetRemote(out error));
//get started on this while we're processing here
var pullsRequest = Task.Factory.StartNew(() => pulls = repo.MergedPullRequests(out error2));
await remoteRequest;
if (remote == null)
{
Repos = new RepositoryCollection
{
{ repoOwner, repoName }
},
State = ItemState.Open,
Type = IssueTypeQualifier.PullRequest
});
MessageBox.Show(String.Format("Error retrieving remote repository: {0}", error));
return;
}
if (!remote.Contains("github.com"))
{
MessageBox.Show("Pull request support is only available for github based repositories!", "Error");
return;
}
//Assume standard gh format: [(git)|(https)]://github.com/owner/repo(.git)[0-1]
var splits = remote.Split('/');
var repoName = splits[splits.Length - 1];
var repoOwner = splits[splits.Length - 2];
//Search for open PRs
Enabled = false;
UseWaitCursor = true;
SearchIssuesResult result;
try
{
result = await client.Search.SearchIssues(new SearchIssuesRequest
{
Repos = new RepositoryCollection { { repoOwner, repoName } },
State = ItemState.Open,
Type = IssueTypeQualifier.PullRequest
});
}
finally
{
Enabled = true;
UseWaitCursor = false;
}
//now we need to know what's merged
await WrapServerOp(() => pullsRequest.Wait());
if (pulls == null)
MessageBox.Show(String.Format("Error retrieving currently merged pull requests: {0}", error2));
foreach (var I in result.Items)
PullRequestListBox.Items.Add(String.Format("#{0} - {1}", I.Number, I.Title), pulls.Any(x => x.Number == I.Number));
}
finally
{
Enabled = true;
UseWaitCursor = false;
}
//now we need to know what's merged
await WrapServerOp(() => pullsRequest.Wait());
if (pulls == null)
MessageBox.Show(String.Format("Error retrieving currently merged pull requests: {0}", error2));
foreach (var I in result.Items)
PullRequestListBox.Items.Add(String.Format("#{0} - {1}", I.Number, I.Title), pulls.Any(x => x.Number == I.Number));
}
finally
catch (ForbiddenException)
{
Enabled = true;
UseWaitCursor = false;
if (client.Credentials.AuthenticationType == AuthenticationType.Anonymous) //assume request limit hit
{
if (MessageBox.Show("You seem to have hit the rate limit of 60 requests per hour of the GitHub API for anonymous requests. Would you like to enter credentials to bypass this?", "Rate limited", MessageBoxButtons.YesNo) != DialogResult.Yes)
return;
using (var D = new GitHubLoginPrompt(client))
if (D.ShowDialog() == DialogResult.OK)
LoadPullRequests(client);
}
else
throw;
}
}
@@ -112,7 +130,7 @@ namespace TGControlPanel
/// <param name="e">The <see cref="EventArgs"/></param>
void PullRequestManager_Load(object sender, EventArgs e)
{
LoadPullRequests();
LoadPullRequests(null);
}
/// <summary>
@@ -202,7 +220,7 @@ namespace TGControlPanel
/// <param name="e">The <see cref="EventArgs"/></param>
void RefreshButton_Click(object sender, EventArgs e)
{
LoadPullRequests();
LoadPullRequests(null);
}
}
}