Skip to content

Commit 0698029

Browse files
committed
Starting on #3 switching to data binding
Move to codicon icons for status Added query limit option Added progress indicator for load
1 parent 170b72a commit 0698029

10 files changed

+201
-37
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Globalization;
2+
using System.Windows.Data;
3+
using System.Windows.Media;
4+
5+
namespace GitHubActionsVS.Converters;
6+
7+
public class ConclusionColorConverter : IValueConverter
8+
{
9+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
10+
{
11+
string status = value as string;
12+
return GetConclusionColor(status);
13+
}
14+
15+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
16+
{
17+
return value;
18+
}
19+
20+
private SolidColorBrush GetConclusionColor(string status) => status.ToLowerInvariant() switch
21+
{
22+
"success" => new SolidColorBrush(Colors.Green),
23+
"failure" => new SolidColorBrush(Colors.Red),
24+
_ => new SolidColorBrush(Colors.Black),
25+
};
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Globalization;
2+
using System.Windows.Data;
3+
4+
namespace GitHubActionsVS.Converters;
5+
public class ConclusionIconConverter : IValueConverter
6+
{
7+
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
8+
{
9+
string status = value as string;
10+
return GetConclusionIndicator(status);
11+
}
12+
13+
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
14+
{
15+
return value;
16+
}
17+
18+
private string GetConclusionIndicator(string status) => status.ToLowerInvariant() switch
19+
{
20+
"success" => "\uEBB3 ",
21+
"failure" => "\uEBB4 ",
22+
"cancelled" => "\uEABD ",
23+
"skipped" => "\uEABD ",
24+
_ => "🤷🏽",
25+
};
26+
}

src/GitHubActionsVS.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,14 @@
4747
<ItemGroup>
4848
<Compile Include="Commands\GotoRepoCommand.cs" />
4949
<Compile Include="Commands\RefreshRepoCommand.cs" />
50+
<Compile Include="Converters\ConclusionColorConverter.cs" />
51+
<Compile Include="Converters\ConclusionIconConverter.cs" />
5052
<Compile Include="Helpers\CredentialManager.cs" />
5153
<Compile Include="Helpers\RepoInfo.cs" />
54+
<Compile Include="Models\BaseWorkflowType.cs" />
55+
<Compile Include="Models\SimpleJob.cs" />
56+
<Compile Include="Models\SimpleRun.cs" />
57+
<Compile Include="Options\ExtensionOptions.cs" />
5258
<Compile Include="Properties\AssemblyInfo.cs" />
5359
<Compile Include="Commands\ActionsToolWindowCommand.cs" />
5460
<Compile Include="GitHubActionsVSPackage.cs" />

src/GitHubActionsVSPackage.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
namespace GitHubActionsVS;
1212
[PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
1313
[ProvideToolWindow(typeof(ActionsToolWindow.Pane), Style = VsDockStyle.Tabbed, Window = WindowGuids.SolutionExplorer)]
14+
[ProvideOptionPage(typeof(OptionsProvider.ExtensionOptionsOptions), "GitHub Actions for VS", "General", 0, 0, true, SupportsProfiles = true)]
1415
[ProvideMenuResource("Menus.ctmenu", 1)]
1516
[Guid(PackageGuids.GitHubActionsVSString)]
1617
[ProvideBindingPath]

src/Models/BaseWorkflowType.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace GitHubActionsVS.Models;
2+
3+
public abstract class BaseWorkflowType
4+
{
5+
public string Name { get; set; }
6+
7+
public abstract string DisplayName { get; }
8+
public string Conclusion { get; set; }
9+
public DateTimeOffset? LogDate { get; set; }
10+
public string DisplayDate => $"{LogDate:g}";
11+
public string? Url { get; set; }
12+
public string Id { get; set; }
13+
}

src/Models/SimpleJob.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace GitHubActionsVS.Models;
2+
3+
public class SimpleJob : SimpleRun
4+
{
5+
public override string DisplayName => Name;
6+
}

src/Models/SimpleRun.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
using System.Collections.Generic;
2+
3+
namespace GitHubActionsVS.Models;
4+
public class SimpleRun : BaseWorkflowType
5+
{
6+
public List<SimpleJob> Jobs { get; set; }
7+
public string RunNumber { get; set; }
8+
9+
public override string DisplayName => $"{Name} #{RunNumber}";
10+
}

src/Options/ExtensionOptions.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.ComponentModel;
2+
using System.Runtime.InteropServices;
3+
4+
namespace GitHubActionsVS;
5+
internal partial class OptionsProvider
6+
{
7+
// Register the options with this attribute on your package class:
8+
// [ProvideOptionPage(typeof(OptionsProvider.ExtensionOptionsOptions), "GitHubActionsVS", "ExtensionOptions", 0, 0, true, SupportsProfiles = true)]
9+
[ComVisible(true)]
10+
public class ExtensionOptionsOptions : BaseOptionPage<ExtensionOptions> { }
11+
}
12+
13+
public class ExtensionOptions : BaseOptionModel<ExtensionOptions>
14+
{
15+
[Category("Query Settings")]
16+
[DisplayName("Max Runs")]
17+
[Description("The maximum number of runs to retrieve")]
18+
[DefaultValue(10)]
19+
public int MaxRuns { get; set; } = 10;
20+
}

src/ToolWindows/GHActionsToolWindow.xaml

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,16 @@
99
xmlns:catalog="clr-namespace:Microsoft.VisualStudio.Imaging;assembly=Microsoft.VisualStudio.ImageCatalog"
1010
xmlns:toolkit="clr-namespace:Community.VisualStudio.Toolkit;assembly=Community.VisualStudio.Toolkit"
1111
xmlns:emoji="clr-namespace:Emoji.Wpf;assembly=Emoji.Wpf"
12+
xmlns:ivc="clr-namespace:GitHubActionsVS.Converters"
1213
toolkit:Themes.UseVsTheme="True"
1314
mc:Ignorable="d"
1415
d:DesignHeight="300"
1516
d:DesignWidth="300"
1617
Name="GHActionToolWindow">
1718
<UserControl.Resources>
1819
<FontFamily x:Key="CodiconFont">pack://application:,,,/GitHubActionsVS;component/Resources/#codicon</FontFamily>
20+
<ivc:ConclusionIconConverter x:Key="ConclusionIconConverter" />
21+
<ivc:ConclusionColorConverter x:Key="ConclusionColorConverter" />
1922
<Style TargetType="{x:Type Expander}">
2023
<Setter Property="toolkit:Themes.UseVsTheme" Value="True" />
2124
</Style>
@@ -40,12 +43,31 @@
4043
<TextBlock Text="{Binding}" Margin="5,0" />
4144
</StackPanel>
4245
</DataTemplate>
46+
<HierarchicalDataTemplate x:Key="TreeViewRunNodeDataTemplate" ItemsSource="{Binding Jobs}">
47+
<StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Grid.Column="0">
48+
<TextBlock VerticalAlignment="Center" FontFamily="{StaticResource CodiconFont}"
49+
Foreground="{Binding Path=Conclusion, Converter={StaticResource ConclusionColorConverter}}"
50+
Text="{Binding Path=Conclusion, Converter={StaticResource ConclusionIconConverter}}"/>
51+
<emoji:TextBlock Text="{Binding DisplayName}" VerticalAlignment="Bottom" />
52+
</StackPanel>
53+
</HierarchicalDataTemplate>
4354
</UserControl.Resources>
44-
<Grid Margin="5,5,0,0">
45-
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
55+
<Grid>
56+
<Grid.RowDefinitions>
57+
<RowDefinition Height="Auto" />
58+
<RowDefinition Height="*" />
59+
</Grid.RowDefinitions>
60+
<ProgressBar x:Name="refreshProgress" Height="5" Grid.Row="0" Visibility="Collapsed" />
61+
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" Grid.Row="1" Margin="5,5,0,0">
4662
<StackPanel Orientation="Vertical">
4763
<Expander Header="Current Branch">
48-
<TreeView x:Name="tvCurrentBranch" BorderThickness="0" />
64+
<TreeView BorderThickness="0" PreviewMouseWheel="HandlePreviewMouseWheel" MouseDoubleClick="JobItem_MouseDoubleClick" x:Name="tvCurrentBranch" ItemTemplate="{DynamicResource TreeViewRunNodeDataTemplate}" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch">
65+
<TreeView.Resources>
66+
<Style TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource {x:Type TreeViewItem}}">
67+
<EventSetter Event="MouseDoubleClick" Handler="JobItem_MouseDoubleClick"/>
68+
</Style>
69+
</TreeView.Resources>
70+
</TreeView>
4971
</Expander>
5072
<!--<Expander Header="Workflows">
5173
<TreeView x:Name="tvWorkflows" BorderThickness="0"/>

src/ToolWindows/GHActionsToolWindow.xaml.cs

Lines changed: 68 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
using GitHubActionsVS.Helpers;
2+
using GitHubActionsVS.Models;
23
using GitHubActionsVS.ToolWindows;
34
using Octokit;
5+
using System.Collections.Generic;
46
using System.Diagnostics;
57
using System.Windows;
68
using System.Windows.Controls;
9+
using System.Windows.Input;
710

811
namespace GitHubActionsVS;
912

@@ -85,12 +88,20 @@ private void ClearTreeViews()
8588
{
8689
tvSecrets.Items.Clear();
8790
//tvWorkflows.Items.Clear();
88-
tvCurrentBranch.Items.Clear();
91+
tvCurrentBranch.ItemsSource = null;
8992
tvEnvironments.Items.Clear();
9093
}
9194

9295
private async Task LoadDataAsync()
9396
{
97+
98+
// get the settings
99+
var generalSettings = await ExtensionOptions.GetLiveInstanceAsync();
100+
var maxRuns = generalSettings.MaxRuns;
101+
102+
refreshProgress.IsIndeterminate = true;
103+
refreshProgress.Visibility = Visibility.Visible;
104+
94105
GitHubClient client = GetGitHubClient();
95106

96107
try
@@ -122,46 +133,65 @@ private async Task LoadDataAsync()
122133
//}
123134

124135
// get current branch
125-
var runs = await client.Actions?.Workflows?.Runs?.List(_repoInfo.RepoOwner, _repoInfo.RepoName, new WorkflowRunsRequest() { Branch = _repoInfo.CurrentBranch }, new ApiOptions() { PageCount = 2, PageSize = 10 });
136+
var runs = await client.Actions?.Workflows?.Runs?.List(_repoInfo.RepoOwner, _repoInfo.RepoName, new WorkflowRunsRequest() { Branch = _repoInfo.CurrentBranch }, new ApiOptions() { PageCount = 1, PageSize = maxRuns });
137+
138+
// creating simplified model of the GH info for the treeview
139+
List<SimpleRun> runsList = new List<SimpleRun>();
140+
141+
// iterate throught the runs
126142
foreach (var run in runs.WorkflowRuns)
127143
{
128-
var item = new TreeViewItem
144+
SimpleRun simpleRun = new()
129145
{
130-
Header = CreateEmojiContent($"{GetConclusionIndicator(run.Conclusion.Value.StringValue)} {run.Name} #{run.RunNumber}"),
131-
Tag = run
146+
Conclusion = run.Conclusion.Value.StringValue,
147+
Name = run.Name,
148+
LogDate = run.UpdatedAt,
149+
Id = run.Id.ToString(),
150+
RunNumber = run.RunNumber.ToString()
132151
};
133152

134-
// iterate through the run
135-
var jobs = await client.Actions?.Workflows?.Jobs?.List(_repoInfo.RepoOwner, _repoInfo.RepoName, run.Id);
153+
// get the jobs for the run
154+
var jobs = await client.Actions.Workflows.Jobs?.List(_repoInfo.RepoOwner, _repoInfo.RepoName, run.Id);
155+
156+
List<SimpleJob> simpleJobs = new();
157+
158+
// iterate through the jobs' steps
136159
foreach (var job in jobs.Jobs)
137160
{
138-
var jobItem = new TreeViewItem
139-
{
140-
Header = CreateEmojiContent($"{GetConclusionIndicator(job.Conclusion.Value.StringValue)} {job.Name}"),
141-
Tag = job
142-
};
143-
144-
// iterate through the job
161+
List<SimpleJob> steps = new();
145162
foreach (var step in job.Steps)
146163
{
147-
var stepItem = new TreeViewItem
164+
steps.Add(new SimpleJob()
148165
{
149-
Header = CreateEmojiContent($"{GetConclusionIndicator(step.Conclusion.Value.StringValue)}: {step.Name}"),
150-
Tag = $"{job.HtmlUrl}#step:{step.Number.ToString()}:1" //https://github.com/timheuer/workflow-playground/actions/runs/5548963145/jobs/10132505381#step:2:7
151-
};
152-
stepItem.MouseDoubleClick += JobItem_MouseDoubleClick;
153-
jobItem.Items.Add(stepItem);
166+
Conclusion = step.Conclusion.Value.StringValue,
167+
Name = step.Name,
168+
Url = $"{job.HtmlUrl}#step:{step.Number.ToString()}:1"
169+
});
154170
}
155-
156-
item.Items.Add(jobItem);
171+
simpleJobs.Add(new SimpleJob()
172+
{
173+
Conclusion = job.Conclusion.Value.StringValue,
174+
Name = job.Name,
175+
Id = job.Id.ToString(),
176+
Jobs = steps // add the steps to the job
177+
});
157178
}
158-
tvCurrentBranch.Items.Add(item);
179+
180+
// add the jobs to the run
181+
simpleRun.Jobs = simpleJobs;
182+
183+
runsList.Add(simpleRun);
159184
}
185+
186+
tvCurrentBranch.ItemsSource = runsList;
160187
}
161188
catch (Exception ex)
162189
{
163190
Console.WriteLine(ex);
164191
}
192+
193+
refreshProgress.Visibility = Visibility.Collapsed;
194+
refreshProgress.IsIndeterminate = false;
165195
}
166196

167197
private UIElement CreateEmojiContent(string emojiString)
@@ -183,21 +213,25 @@ private static GitHubClient GetGitHubClient()
183213
return client;
184214
}
185215

186-
private string GetConclusionIndicator(string status) => status.ToLowerInvariant() switch
187-
{
188-
"success" => "✅",
189-
"failure" => "❌",
190-
"cancelled" => "🚫",
191-
"skipped" => "⏭",
192-
_ => "🤷🏽",
193-
};
194-
195216
private void JobItem_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
196217
{
197218
// get the items Tag
198-
if (sender is TreeViewItem item && item.Tag is string url)
219+
if (sender is TreeViewItem item && item.Header is SimpleJob job && job.Url is not null)
220+
{
221+
Process.Start(job.Url);
222+
}
223+
}
224+
225+
private void HandlePreviewMouseWheel(object sender, MouseWheelEventArgs e)
226+
{
227+
if (!e.Handled)
199228
{
200-
Process.Start(url);
229+
e.Handled = true;
230+
var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
231+
eventArg.RoutedEvent = UIElement.MouseWheelEvent;
232+
eventArg.Source = sender;
233+
var parent = ((Control)sender).Parent as UIElement;
234+
parent.RaiseEvent(eventArg);
201235
}
202236
}
203237
}

0 commit comments

Comments
 (0)