Skip to content

Commit 6af09fc

Browse files
authored
Improve compiler versions dialog (#82)
2 parents 584dfed + 44f15a1 commit 6af09fc

24 files changed

+978
-215
lines changed

src/App/Lab/CommitHashView.razor

Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
@inject ILocalStorageService LocalStorage
2+
@inject WorkerController Worker
3+
@inject HttpClient Client
4+
5+
@if (alternativeCommit is { } alt)
6+
{
7+
<CommitHashView Enabled="Enabled" Commit="alt.Result" SaveKey="@("Alternative" + SaveKey)" />
8+
@(" / ")
9+
}
10+
11+
<a href="@Commit.Url" target="_blank"
12+
title="@GetTitle()">@Commit.ShortHash</a>
13+
@if (TopLevelDateAndVersion && commitInfo != null)
14+
{
15+
@(" ")
16+
17+
<span title="@commitInfo.Message">(@commitInfo.Date.ToString(dateFormat))</span>
18+
@if (commitInfo.Version != null)
19+
{
20+
@(", ")
21+
22+
<a title="Click to see release notes"
23+
href="@Commit.ReleasesUrl"
24+
target="_blank">@commitInfo.Version</a>
25+
}
26+
}
27+
28+
@code {
29+
private const string dateFormat = "MMM d, yyyy, HH:mm";
30+
private readonly AsyncLock loading = new();
31+
private CommitInfo? commitInfo;
32+
private AlternativeCommitInfo? alternativeCommit;
33+
34+
private readonly struct AlternativeCommitInfo
35+
{
36+
public required CommitLink Result { get; init; }
37+
public required CommitLink For { get; init; }
38+
}
39+
40+
[Parameter, EditorRequired]
41+
public bool Enabled { get; init; }
42+
43+
[Parameter, EditorRequired]
44+
public CommitLink Commit { get; init; }
45+
46+
[Parameter, EditorRequired]
47+
public string SaveKey { get; init; }
48+
49+
[Parameter]
50+
public string? AlternativeCommitHash { get; init; }
51+
52+
[Parameter]
53+
public string? AlternativeRepoUrl { get; init; }
54+
55+
[Parameter]
56+
public bool TopLevelDateAndVersion { get; init; }
57+
58+
public string? GetTitle()
59+
{
60+
if (commitInfo is null || TopLevelDateAndVersion)
61+
{
62+
return commitInfo?.Message;
63+
}
64+
65+
string versionSuffix = commitInfo.Version != null ? $", {commitInfo.Version}" : string.Empty;
66+
67+
return $"{commitInfo.Message} ({commitInfo.Date.ToString(dateFormat)}{versionSuffix})";
68+
}
69+
70+
private sealed class CommitInfo
71+
{
72+
public required string RepoUrl { get; init; }
73+
public required string Hash { get; init; }
74+
public required DateTimeOffset Date { get; init; }
75+
public required string Message { get; init; }
76+
public required string? Version { get; init; }
77+
}
78+
79+
protected override async Task OnParametersSetAsync()
80+
{
81+
if (!Enabled)
82+
{
83+
return;
84+
}
85+
86+
using var _ = await loading.LockAsync();
87+
88+
var tasks = new List<Task>();
89+
90+
// Load commit info from storage (cached) or the web unless already loaded.
91+
if (!Matches(Commit, commitInfo))
92+
{
93+
if (await tryLoadCachedAsync() is { } cached &&
94+
Matches(Commit, cached))
95+
{
96+
commitInfo = cached;
97+
}
98+
else
99+
{
100+
tasks.Add(LoadCommitInfoAsync());
101+
}
102+
}
103+
104+
// Load alternative commit info.
105+
tasks.Add(LoadAlternativeInfoAsync());
106+
107+
await Task.WhenAll(tasks);
108+
109+
async Task<CommitInfo?> tryLoadCachedAsync()
110+
{
111+
try
112+
{
113+
return await LocalStorage.GetItemAsync<CommitInfo>(SaveKey);
114+
}
115+
catch (JsonException)
116+
{
117+
return null;
118+
}
119+
}
120+
}
121+
122+
private async Task LoadCommitInfoAsync()
123+
{
124+
commitInfo = await GetCommitInfoAsync();
125+
StateHasChanged();
126+
if (commitInfo != null)
127+
{
128+
await LocalStorage.SetItemAsync(SaveKey, commitInfo);
129+
}
130+
}
131+
132+
private async Task<CommitInfo?> GetCommitInfoAsync()
133+
{
134+
if (Commit.OwnerAndName is not { } ownerAndName)
135+
{
136+
return null;
137+
}
138+
139+
GitHubCommitResponse? response;
140+
try
141+
{
142+
response = await Client.GetFromJsonAsync(
143+
$"https://api.github.com/repos/{ownerAndName}/commits/{Commit.Hash}",
144+
SettingsJsonContext.Default.GitHubCommitResponse);
145+
if (response is null)
146+
{
147+
return null;
148+
}
149+
}
150+
catch (HttpRequestException)
151+
{
152+
return null;
153+
}
154+
155+
// Find version tag corresponding to the commit.
156+
// Inspired by https://stackoverflow.com/a/69590375.
157+
string? version = null;
158+
try
159+
{
160+
var request = new HttpRequestMessage(
161+
HttpMethod.Get,
162+
$"https://github.com/{ownerAndName}/branch_commits/{Commit.Hash}".WithCorsProxy());
163+
request.Headers.Accept.Add(new("application/json"));
164+
var tagsResponse = await Client.SendAsync(request);
165+
var tagsObj = await tagsResponse.Content.ReadFromJsonAsync(SettingsJsonContext.Default.GitHubBranchCommitsResponse);
166+
version = tagsObj?.Tags is [{ Length: > 0 } tag] ? tag : null;
167+
}
168+
catch (HttpRequestException) { }
169+
catch (JsonException) { }
170+
171+
return new CommitInfo
172+
{
173+
RepoUrl = Commit.RepoUrl,
174+
Hash = Commit.Hash,
175+
Date = response.Commit.Author.Date,
176+
Message = response.Commit.Message.GetFirstLine(),
177+
Version = version,
178+
};
179+
}
180+
181+
private async Task LoadAlternativeInfoAsync()
182+
{
183+
var saveKey = SaveKey + "_AlternativeCommitLink";
184+
if (alternativeCommit is null)
185+
{
186+
alternativeCommit = await LocalStorage.GetItemAsync<AlternativeCommitInfo?>(saveKey);
187+
}
188+
189+
if ((AlternativeRepoUrl is null || AlternativeRepoUrl == Commit.RepoUrl) &&
190+
(AlternativeCommitHash is null || AlternativeCommitHash == Commit.Hash))
191+
{
192+
alternativeCommit = null;
193+
}
194+
else if (alternativeCommit is { } alt &&
195+
alt.Result.RepoUrl == AlternativeRepoUrl &&
196+
alt.Result.Hash == AlternativeCommitHash &&
197+
Matches(alt.For, commitInfo))
198+
{
199+
// Already loaded.
200+
return;
201+
}
202+
else if ((AlternativeCommitHash ?? await Worker.TryGetSubRepoCommitHashAsync(Commit.Hash, AlternativeRepoUrl!)) is not { } altCommitHash)
203+
{
204+
alternativeCommit = null;
205+
}
206+
else
207+
{
208+
alternativeCommit = new()
209+
{
210+
Result = new CommitLink
211+
{
212+
RepoUrl = AlternativeRepoUrl ?? Commit.RepoUrl,
213+
Hash = altCommitHash,
214+
},
215+
For = Commit,
216+
};
217+
}
218+
219+
StateHasChanged();
220+
221+
await LocalStorage.SetItemAsync(saveKey, alternativeCommit);
222+
}
223+
224+
private static bool Matches(CommitLink commit, CommitInfo? info)
225+
{
226+
return info != null &&
227+
info.Hash == commit.Hash &&
228+
info.RepoUrl == commit.RepoUrl;
229+
}
230+
}

src/App/Lab/InputOutputCache.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public async Task StoreAsync(SavedState state, CompiledAssembly output)
4545
return null;
4646
}
4747

48-
if (await response.Content.ReadFromJsonAsync<CompiledAssembly>(WorkerJsonContext.Default.Options) is not { } output)
48+
if (await response.Content.ReadFromJsonAsync(WorkerJsonContext.Default.CompiledAssembly) is not { } output)
4949
{
5050
logger.LogError("No output.");
5151
return null;

0 commit comments

Comments
 (0)