Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 37 additions & 3 deletions app/lib/services/dependency_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -422,10 +422,12 @@ class DependencyManager {
expectedSha256: platformInfo.sha256,
);

// Extract
// Extract. _extractZip emits per-file extraction progress; this initial
// event (0/0 -> indeterminate) covers the synchronous decode that precedes
// the first file write.
_progressController.add(DownloadProgress(
bytesReceived: platformInfo.size ?? 0,
totalBytes: platformInfo.size ?? 0,
bytesReceived: 0,
totalBytes: 0,
status: 'Extracting...',
));

Expand Down Expand Up @@ -528,6 +530,28 @@ class DependencyManager {
final bytes = await zipFile.readAsBytes();
final archive = ZipDecoder().decodeBytes(bytes);

// Total uncompressed size, for extraction progress. Extraction (many file
// writes + chmod) is slow on Linux, so report progress rather than leaving
// the dialog frozen on "Extracting...".
var totalBytes = 0;
for (final file in archive) {
if (file.isFile) totalBytes += file.size;
}

var extracted = 0;
var lastEmitted = -1;
const emitEvery = 2 * 1024 * 1024; // throttle UI updates to ~every 2 MB

void emitExtractProgress() {
_progressController.add(DownloadProgress(
bytesReceived: extracted,
totalBytes: totalBytes,
status: 'Extracting...',
));
}

emitExtractProgress();

for (final file in archive) {
final filePath = path.join(depsDir.path, file.name);

Expand All @@ -540,11 +564,21 @@ class DependencyManager {
if (!Platform.isWindows && _isExecutable(file.name)) {
await Process.run('chmod', ['+x', filePath]);
}

extracted += file.size;
if (extracted - lastEmitted >= emitEvery) {
lastEmitted = extracted;
emitExtractProgress();
}
} else {
await Directory(filePath).create(recursive: true);
}
}

// Final 100% extraction tick.
extracted = totalBytes;
emitExtractProgress();

// On macOS, remove quarantine attribute and re-sign binaries for Gatekeeper
if (Platform.isMacOS) {
await _removeQuarantine(depsDir.path);
Expand Down
16 changes: 10 additions & 6 deletions app/lib/views/dependency_download_dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -155,22 +155,26 @@ class _DependencyDownloadDialogState extends State<DependencyDownloadDialog> {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(_progress!.status),
Text(_progress!.progressPercent),
if (_progress!.totalBytes > 0) Text(_progress!.progressPercent),
],
),
const SizedBox(height: 8),
ClipRRect(
borderRadius: BorderRadius.circular(4),
// Indeterminate (animated) when we don't yet have a fraction —
// e.g. connecting, or the zip-decode step before extraction
// byte progress starts — otherwise a determinate bar.
child: LinearProgressIndicator(
value: _progress!.progress,
value: _progress!.progress > 0 ? _progress!.progress : null,
minHeight: 8,
),
),
const SizedBox(height: 8),
Text(
'${_formatBytes(_progress!.bytesReceived)} / ${_formatBytes(_progress!.totalBytes)}',
style: Theme.of(context).textTheme.bodySmall,
),
if (_progress!.totalBytes > 0)
Text(
'${_formatBytes(_progress!.bytesReceived)} / ${_formatBytes(_progress!.totalBytes)}',
style: Theme.of(context).textTheme.bodySmall,
),
] else ...[
const LinearProgressIndicator(),
const SizedBox(height: 8),
Expand Down
Loading