Skip to content
This repository was archived by the owner on Dec 5, 2024. It is now read-only.

Commit 2270f0e

Browse files
committed
Loading spinner and progress reporting
1 parent 0f5db5f commit 2270f0e

27 files changed

+586
-179
lines changed

src/GitHub.Api/Application/ApplicationManagerBase.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ abstract class ApplicationManagerBase : IApplicationManager
1212
protected static ILogging Logger { get; } = LogHelper.GetLogger<IApplicationManager>();
1313

1414
private RepositoryManager repositoryManager;
15+
protected bool isBusy;
16+
public event Action<IProgress> OnProgress;
1517

1618
public ApplicationManagerBase(SynchronizationContext synchronizationContext)
1719
{
@@ -57,9 +59,11 @@ public void Run(bool firstRun)
5759
{
5860
Logger.Trace("No git path found in settings");
5961

62+
isBusy = true;
6063
var initEnvironmentTask = new ActionTask<NPath>(CancellationToken, (b, path) => InitializeEnvironment(path)) { Affinity = TaskAffinity.UI };
6164
var findExecTask = new FindExecTask("git", CancellationToken)
62-
.FinallyInUI((b, ex, path) => {
65+
.FinallyInUI((b, ex, path) =>
66+
{
6367
if (b && path != null)
6468
{
6569
Logger.Trace("FindExecTask Success: {0}", path);
@@ -70,6 +74,7 @@ public void Run(bool firstRun)
7074
Logger.Warning("FindExecTask Failure");
7175
Logger.Error("Git not found");
7276
}
77+
isBusy = false;
7378
});
7479

7580
var applicationDataPath = Environment.GetSpecialFolder(System.Environment.SpecialFolder.LocalApplicationData).ToNPath();
@@ -190,7 +195,7 @@ private void InitializeEnvironment(NPath gitExecutablePath)
190195
}
191196
else
192197
{
193-
Logger.Warning("No Windows CredentialHeloper found: Setting to wincred");
198+
Logger.Warning("No Windows CredentialHelper found: Setting to wincred");
194199

195200
GitClient.SetConfig("credential.helper", "wincred", GitConfigSource.Global)
196201
.Then(afterGitSetup)
@@ -234,6 +239,7 @@ public void Dispose()
234239
public ISettings SystemSettings { get; protected set; }
235240
public ISettings UserSettings { get; protected set; }
236241
public IUsageTracker UsageTracker { get; protected set; }
242+
public bool IsBusy { get { return isBusy || RepositoryManager.IsBusy; } }
237243
protected TaskScheduler UIScheduler { get; private set; }
238244
protected SynchronizationContext SynchronizationContext { get; private set; }
239245
protected IRepositoryManager RepositoryManager { get { return repositoryManager; } }

src/GitHub.Api/Application/IApplicationManager.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ public interface IApplicationManager : IDisposable
1717
ITaskManager TaskManager { get; }
1818
IGitClient GitClient { get; }
1919
IUsageTracker UsageTracker { get; }
20-
20+
bool IsBusy { get; }
2121
void Run(bool firstRun);
2222
void RestartRepository();
2323
ITask InitializeRepository();
24+
event Action<IProgress> OnProgress;
2425
}
2526
}

src/GitHub.Api/Installer/GitInstaller.cs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ class GitInstaller
7777
private readonly IZipHelper sharpZipLibHelper;
7878
private NPath gitArchiveFilePath;
7979
private NPath gitLfsArchivePath;
80+
private Progress progress = new Progress();
81+
public event Action<IProgress> OnProgress;
8082

8183
public GitInstaller(IEnvironment environment, CancellationToken cancellationToken,
8284
GitInstallDetails installDetails)
@@ -115,6 +117,9 @@ public void SetupGitIfNeeded(ActionTask<NPath> onSuccess, ITask onFailure)
115117
if (IsGitExtracted())
116118
{
117119
Logger.Trace("SetupGitIfNeeded: Skipped");
120+
progress.Total = 100;
121+
progress.Value = 100;
122+
OnProgress?.Invoke(progress);
118123
onSuccess.PreviousResult = installDetails.GitExecutablePath;
119124
onSuccess.Start();
120125
}
@@ -127,6 +132,7 @@ public void SetupGitIfNeeded(ActionTask<NPath> onSuccess, ITask onFailure)
127132

128133
private void ExtractPortableGit(ActionTask<NPath> onSuccess, ITask onFailure)
129134
{
135+
Logger.Trace("ExtractPortableGit");
130136
ITask downloadFilesTask = null;
131137
if ((gitArchiveFilePath == null) || (gitLfsArchivePath == null))
132138
{
@@ -138,7 +144,19 @@ private void ExtractPortableGit(ActionTask<NPath> onSuccess, ITask onFailure)
138144
var gitLfsExtractPath = tempZipExtractPath.Combine("git-lfs").CreateDirectory();
139145

140146
var resultTask = new UnzipTask(cancellationToken, gitArchiveFilePath, gitExtractPath, sharpZipLibHelper, environment.FileSystem, GitInstallDetails.GitExtractedMD5)
147+
.Progress(p =>
148+
{
149+
progress.Task = p.Task;
150+
var pt = p.Value / p.Total;
151+
progress.Value = 40 + 20 * pt;
152+
})
141153
.Then(new UnzipTask(cancellationToken, gitLfsArchivePath, gitLfsExtractPath, sharpZipLibHelper, environment.FileSystem, GitInstallDetails .GitLfsExtractedMD5))
154+
.Progress(p =>
155+
{
156+
progress.Task = p.Task;
157+
var pt = p.Value / p.Total;
158+
progress.Value = 60 + 20 * pt;
159+
})
142160
.Then(s => MoveGitAndLfs(gitExtractPath, gitLfsExtractPath, tempZipExtractPath));
143161

144162
resultTask.Then(onFailure, TaskRunOptions.OnFailure);
@@ -154,6 +172,9 @@ private void ExtractPortableGit(ActionTask<NPath> onSuccess, ITask onFailure)
154172

155173
private NPath MoveGitAndLfs(NPath gitExtractPath, NPath gitLfsExtractPath, NPath tempZipExtractPath)
156174
{
175+
progress.Value = 80;
176+
OnProgress?.Invoke(progress);
177+
157178
var targetGitLfsExecPath = installDetails.GetGitLfsExecutablePath(gitExtractPath);
158179
var extractGitLfsExePath = gitLfsExtractPath.Combine(installDetails.GitLfsExecutable);
159180

@@ -163,6 +184,9 @@ private NPath MoveGitAndLfs(NPath gitExtractPath, NPath gitLfsExtractPath, NPath
163184

164185
Logger.Trace($"Moving tempDirectory:'{gitExtractPath}' to extractTarget:'{installDetails.GitInstallationPath}'");
165186

187+
progress.Value = 90;
188+
OnProgress?.Invoke(progress);
189+
166190
installDetails.GitInstallationPath.EnsureParentDirectoryExists();
167191
gitExtractPath.Move(installDetails.GitInstallationPath);
168192

@@ -182,22 +206,46 @@ private ITask CreateDownloadTask()
182206
gitLfsArchivePath = tempZipPath.Combine("git-lfs.zip");
183207

184208
var downloadGitMd5Task = new DownloadTextTask(TaskManager.Instance.Token, environment.FileSystem,
185-
installDetails.GitZipMd5Url, tempZipPath);
209+
installDetails.GitZipMd5Url, tempZipPath)
210+
.Progress(p =>
211+
{
212+
progress.Task = p.Task;
213+
var pt = p.Value / p.Total;
214+
progress.Value = 10 * pt;
215+
});
186216

187217
var downloadGitTask = new DownloadTask(TaskManager.Instance.Token, environment.FileSystem,
188-
installDetails.GitZipUrl, tempZipPath);
218+
installDetails.GitZipUrl, tempZipPath)
219+
.Progress(p =>
220+
{
221+
progress.Task = p.Task;
222+
var pt = p.Value / p.Total;
223+
progress.Value = 10 + 10 * pt;
224+
});
189225

190226
var downloadGitLfsMd5Task = new DownloadTextTask(TaskManager.Instance.Token, environment.FileSystem,
191-
installDetails.GitLfsZipMd5Url, tempZipPath);
227+
installDetails.GitLfsZipMd5Url, tempZipPath)
228+
.Progress(p =>
229+
{
230+
progress.Task = p.Task;
231+
var pt = p.Value / p.Total;
232+
progress.Value = 20 + 10 * pt;
233+
});
192234

193235
var downloadGitLfsTask = new DownloadTask(TaskManager.Instance.Token, environment.FileSystem,
194-
installDetails.GitLfsZipUrl, tempZipPath);
236+
installDetails.GitLfsZipUrl, tempZipPath)
237+
.Progress(p =>
238+
{
239+
progress.Task = p.Task;
240+
var pt = p.Value / p.Total;
241+
progress.Value = 30 + 10 * pt;
242+
});
195243

196244
return
197-
downloadGitMd5Task.Then((b, s) => { downloadGitTask.ValidationHash = s; })
245+
downloadGitMd5Task.Then((b, s) => { ((DownloadTask)downloadGitTask).ValidationHash = s; })
198246
.Then(downloadGitTask)
199247
.Then(downloadGitLfsMd5Task)
200-
.Then((b, s) => { downloadGitLfsTask.ValidationHash = s; })
248+
.Then((b, s) => { ((DownloadTask)downloadGitLfsTask).ValidationHash = s; })
201249
.Then(downloadGitLfsTask);
202250
}
203251

src/GitHub.Api/Installer/IZipHelper.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@ namespace GitHub.Unity
66
interface IZipHelper
77
{
88
void Extract(string archive, string outFolder, CancellationToken cancellationToken,
9-
IProgress<float> zipFileProgress = null, IProgress<long> estimatedDurationProgress = null);
9+
Func<long, long, bool> onProgress = null);
1010
}
1111
}

src/GitHub.Api/Installer/UnzipTask.cs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,21 @@ class UnzipTask: TaskBase
1111
private readonly IZipHelper zipHelper;
1212
private readonly IFileSystem fileSystem;
1313
private readonly string expectedMD5;
14-
private readonly IProgress<float> zipFileProgress;
15-
private readonly IProgress<long> estimatedDurationProgress;
1614

17-
public UnzipTask(CancellationToken token, string archiveFilePath, NPath extractedPath, IFileSystem fileSystem, string expectedMD5 = null, IProgress<float> zipFileProgress = null, IProgress<long> estimatedDurationProgress = null) :
18-
this(token, archiveFilePath, extractedPath, ZipHelper.Instance, fileSystem, expectedMD5, zipFileProgress, estimatedDurationProgress)
15+
public UnzipTask(CancellationToken token, string archiveFilePath, NPath extractedPath, IFileSystem fileSystem, string expectedMD5 = null) :
16+
this(token, archiveFilePath, extractedPath, ZipHelper.Instance, fileSystem, expectedMD5)
1917
{
2018

2119
}
2220

23-
public UnzipTask(CancellationToken token, string archiveFilePath, NPath extractedPath, IZipHelper zipHelper, IFileSystem fileSystem, string expectedMD5 = null, IProgress<float> zipFileProgress = null, IProgress<long> estimatedDurationProgress = null)
21+
public UnzipTask(CancellationToken token, string archiveFilePath, NPath extractedPath, IZipHelper zipHelper, IFileSystem fileSystem, string expectedMD5 = null)
2422
: base(token)
2523
{
2624
this.archiveFilePath = archiveFilePath;
2725
this.extractedPath = extractedPath;
2826
this.zipHelper = zipHelper;
2927
this.fileSystem = fileSystem;
3028
this.expectedMD5 = expectedMD5;
31-
this.zipFileProgress = zipFileProgress;
32-
this.estimatedDurationProgress = estimatedDurationProgress;
3329
}
3430

3531
protected override void Run(bool success)
@@ -40,7 +36,12 @@ protected override void Run(bool success)
4036

4137
try
4238
{
43-
zipHelper.Extract(archiveFilePath, extractedPath, Token, zipFileProgress, estimatedDurationProgress);
39+
zipHelper.Extract(archiveFilePath, extractedPath, Token,
40+
(value, total) =>
41+
{
42+
UpdateProgress(value, total);
43+
return !Token.IsCancellationRequested;
44+
});
4445
}
4546
catch (Exception ex)
4647
{

src/GitHub.Api/Installer/ZipHelper.cs

Lines changed: 12 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -23,87 +23,17 @@ public static IZipHelper Instance
2323
}
2424
}
2525

26-
public static bool Copy(Stream source, Stream destination, int chunkSize, long totalSize,
27-
Func<long, long, bool> progress, int progressUpdateRate)
28-
{
29-
var buffer = new byte[chunkSize];
30-
var bytesRead = 0;
31-
long totalRead = 0;
32-
var averageSpeed = -1f;
33-
var lastSpeed = 0f;
34-
var smoothing = 0.005f;
35-
long readLastSecond = 0;
36-
long timeToFinish = 0;
37-
Stopwatch watch = null;
38-
var success = true;
39-
40-
var trackProgress = totalSize > 0 && progress != null;
41-
if (trackProgress)
42-
{
43-
watch = new Stopwatch();
44-
}
45-
46-
do
47-
{
48-
if (trackProgress)
49-
{
50-
watch.Start();
51-
}
52-
53-
bytesRead = source.Read(buffer, 0, chunkSize);
54-
55-
if (trackProgress)
56-
{
57-
watch.Stop();
58-
}
59-
60-
totalRead += bytesRead;
61-
62-
if (bytesRead > 0)
63-
{
64-
destination.Write(buffer, 0, bytesRead);
65-
if (trackProgress)
66-
{
67-
readLastSecond += bytesRead;
68-
if (watch.ElapsedMilliseconds >= progressUpdateRate || totalRead == totalSize)
69-
{
70-
watch.Reset();
71-
lastSpeed = readLastSecond;
72-
readLastSecond = 0;
73-
averageSpeed = averageSpeed < 0f
74-
? lastSpeed
75-
: smoothing * lastSpeed + (1f - smoothing) * averageSpeed;
76-
timeToFinish = Math.Max(1L,
77-
(long)((totalSize - totalRead) / (averageSpeed / progressUpdateRate)));
78-
79-
if (!progress(totalRead, timeToFinish))
80-
{
81-
break;
82-
}
83-
}
84-
}
85-
}
86-
} while (bytesRead > 0);
87-
88-
if (totalRead > 0)
89-
{
90-
destination.Flush();
91-
}
92-
93-
return success;
94-
}
95-
9626
public void Extract(string archive, string outFolder, CancellationToken cancellationToken,
97-
IProgress<float> zipFileProgress = null, IProgress<long> estimatedDurationProgress = null)
27+
Func<long, long, bool> onProgress = null)
9828
{
99-
ExtractZipFile(archive, outFolder, cancellationToken, zipFileProgress, estimatedDurationProgress);
29+
ExtractZipFile(archive, outFolder, cancellationToken, onProgress);
10030
}
10131

10232
public static void ExtractZipFile(string archive, string outFolder, CancellationToken cancellationToken,
103-
IProgress<float> zipFileProgress = null, IProgress<long> estimatedDurationProgress = null)
33+
Func<long, long, bool> onProgress)
10434
{
35+
const int chunkSize = 4096; // 4K is optimum
10536
ZipFile zf = null;
106-
var estimatedDuration = 1L;
10737
var startTime = DateTime.Now;
10838
var processed = 0;
10939
var totalBytes = 0L;
@@ -112,9 +42,11 @@ public static void ExtractZipFile(string archive, string outFolder, Cancellation
11242
{
11343
var fs = File.OpenRead(archive);
11444
zf = new ZipFile(fs);
45+
var totalSize = fs.Length;
11546

11647
foreach (ZipEntry zipEntry in zf)
11748
{
49+
cancellationToken.ThrowIfCancellationRequested();
11850
if (zipEntry.IsDirectory)
11951
{
12052
continue; // Ignore directories
@@ -152,28 +84,16 @@ public static void ExtractZipFile(string archive, string outFolder, Cancellation
15284
var targetFile = new FileInfo(fullZipToPath);
15385
using (var streamWriter = targetFile.OpenWrite())
15486
{
155-
const int chunkSize = 4096; // 4K is optimum
156-
Copy(zipStream, streamWriter, chunkSize, targetFile.Length, (totalRead, timeToFinish) =>
157-
{
158-
estimatedDuration = timeToFinish;
159-
160-
estimatedDurationProgress.Report(estimatedDuration);
161-
zipFileProgress?.Report((float)(totalBytes + totalRead) / targetFile.Length);
162-
return true;
163-
}, 100);
164-
cancellationToken.ThrowIfCancellationRequested();
87+
if (!Utils.Copy(zipStream, streamWriter, targetFile.Length, chunkSize,
88+
progress: (totalRead, timeToFinish) => {
89+
totalBytes += totalRead;
90+
return onProgress(totalBytes, totalSize);
91+
}))
92+
return;
16593
}
16694

16795
targetFile.LastWriteTime = zipEntry.DateTime;
16896
processed++;
169-
totalBytes += zipEntry.Size;
170-
171-
var elapsedMillisecondsPerFile = (DateTime.Now - startTime).TotalMilliseconds / processed;
172-
estimatedDuration = Math.Max(1L, (long)((fs.Length - totalBytes) * elapsedMillisecondsPerFile));
173-
174-
estimatedDurationProgress?.Report(estimatedDuration);
175-
zipFileProgress?.Report((float)processed / zf.Count);
176-
cancellationToken.ThrowIfCancellationRequested();
17797
}
17898
}
17999
finally

src/UnityExtension/Assets/Editor/GitHub.Unity/ApplicationManager.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ protected override void InitializeUI()
3131
Logger.Trace("Restarted {0}", Environment.Repository);
3232
EnvironmentCache.Instance.Flush();
3333

34+
isBusy = false;
3435
ProjectWindowInterface.Initialize(Environment.Repository);
3536
var window = Window.GetWindow();
3637
if (window != null)

src/UnityExtension/Assets/Editor/GitHub.Unity/EntryPoint.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,9 @@ private static void Initialize()
6262
Debug.LogFormat("Initialized GitHub for Unity version {0}{1}Log file: {2}", ApplicationInfo.Version, Environment.NewLine, logPath);
6363
}
6464

65-
LogHelper.LogAdapter = new FileLogAdapter(logPath);
65+
LogHelper.LogAdapter = new MultipleLogAdapter(new FileLogAdapter(logPath)
66+
, new UnityLogAdapter()
67+
);
6668
LogHelper.Info("Initializing GitHub for Unity version " + ApplicationInfo.Version);
6769

6870
ApplicationManager.Run(ApplicationCache.Instance.FirstRun);

0 commit comments

Comments
 (0)