Merge pull request #107 from RobotsAndPencils/matt/downloadStats

Display installation steps with download stats in info pane
This commit is contained in:
Brandon Evans 2021-02-18 20:35:06 -07:00 committed by GitHub
commit 69f5b707fa
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 250 additions and 82 deletions

View file

@ -75,7 +75,6 @@
CABFAA432593104F00380FEE /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFAA422593104F00380FEE /* AboutView.swift */; };
CABFAA492593162500380FEE /* Bundle+InfoPlistValues.swift in Sources */ = {isa = PBXBuildFile; fileRef = CABFAA482593162500380FEE /* Bundle+InfoPlistValues.swift */; };
CAC28188259EE27200B8AB0B /* CombineExpectations in Frameworks */ = {isa = PBXBuildFile; productRef = CAC28187259EE27200B8AB0B /* CombineExpectations */; };
CAC281C8259F97E100B8AB0B /* InstallationStepView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC281C7259F97E100B8AB0B /* InstallationStepView.swift */; };
CAC281CD259F97FA00B8AB0B /* ObservingProgressIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC281CC259F97FA00B8AB0B /* ObservingProgressIndicator.swift */; };
CAC281DA259F985100B8AB0B /* InstallationStep.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC281D9259F985100B8AB0B /* InstallationStep.swift */; };
CAC281E2259FA44600B8AB0B /* Bundle+XcodesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAC281E1259FA44600B8AB0B /* Bundle+XcodesTests.swift */; };
@ -99,7 +98,10 @@
CAFE4AB425B7D3AF0064FE51 /* AdvancedPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFE4AB325B7D3AF0064FE51 /* AdvancedPreferencePane.swift */; };
CAFE4ABC25B7D54B0064FE51 /* UpdatesPreferencePane.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */; };
CAFFFED8259CDA5000903F81 /* XcodeListViewRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */; };
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */ = {isa = PBXBuildFile; fileRef = E87DD6EA25D053FA00D86808 /* Progress+.swift */; };
E8977EA325C11E1500835F80 /* PreferencesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8977EA225C11E1500835F80 /* PreferencesView.swift */; };
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */; };
E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -227,7 +229,6 @@
CABFAA2B2592FBFC00380FEE /* Configure.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Configure.swift; path = Xcodes/Backend/Configure.swift; sourceTree = SOURCE_ROOT; };
CABFAA422593104F00380FEE /* AboutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutView.swift; sourceTree = "<group>"; };
CABFAA482593162500380FEE /* Bundle+InfoPlistValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+InfoPlistValues.swift"; sourceTree = "<group>"; };
CAC281C7259F97E100B8AB0B /* InstallationStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepView.swift; sourceTree = "<group>"; };
CAC281CC259F97FA00B8AB0B /* ObservingProgressIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservingProgressIndicator.swift; sourceTree = "<group>"; };
CAC281D9259F985100B8AB0B /* InstallationStep.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStep.swift; sourceTree = "<group>"; };
CAC281E1259FA44600B8AB0B /* Bundle+XcodesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+XcodesTests.swift"; sourceTree = "<group>"; };
@ -247,7 +248,7 @@
CAE42486259A68A300B8B246 /* XcodeListCategory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListCategory.swift; sourceTree = "<group>"; };
CAE4248B259A68B800B8B246 /* Optional+IsNotNil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+IsNotNil.swift"; sourceTree = "<group>"; };
CAE424B3259A764700B8B246 /* AppState+Install.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AppState+Install.swift"; sourceTree = "<group>"; };
CAFBC3FF259AC17F00E2A3D8 /* InstallationStepView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepView.swift; sourceTree = "<group>"; };
CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepRowView.swift; sourceTree = "<group>"; };
CAFBC421259ACF8000E2A3D8 /* ObservingProgressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservingProgressView.swift; sourceTree = "<group>"; };
CAFBDB902598FE80003DCC5A /* SelectedXcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedXcode.swift; sourceTree = "<group>"; };
CAFBDB942598FE96003DCC5A /* FocusedValues.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusedValues.swift; sourceTree = "<group>"; };
@ -260,7 +261,9 @@
CAFE4ABB25B7D54B0064FE51 /* UpdatesPreferencePane.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdatesPreferencePane.swift; sourceTree = "<group>"; };
CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XcodeListViewRow.swift; sourceTree = "<group>"; };
CAFFFEEE259CEAC400903F81 /* RingProgressViewStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RingProgressViewStyle.swift; sourceTree = "<group>"; };
E87DD6EA25D053FA00D86808 /* Progress+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Progress+.swift"; sourceTree = "<group>"; };
E8977EA225C11E1500835F80 /* PreferencesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesView.swift; sourceTree = "<group>"; };
E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstallationStepDetailView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@ -387,14 +390,12 @@
isa = PBXGroup;
children = (
CA39711824495F0E00AFFB77 /* AppStoreButtonStyle.swift */,
CAFBDC67259A308B003DCC5A /* InfoPane.swift */,
CAFBC3FF259AC17F00E2A3D8 /* InstallationStepView.swift */,
CAFBC3FF259AC17F00E2A3D8 /* InstallationStepRowView.swift */,
CAFBDC4D2599B33D003DCC5A /* MainToolbar.swift */,
CA44901E2463AD34003D8213 /* Tag.swift */,
CAE42486259A68A300B8B246 /* XcodeListCategory.swift */,
CAD2E7A32449574E00113D76 /* XcodeListView.swift */,
CAFFFED7259CDA5000903F81 /* XcodeListViewRow.swift */,
CAC281C7259F97E100B8AB0B /* InstallationStepView.swift */,
);
path = XcodeList;
sourceTree = "<group>";
@ -437,6 +438,7 @@
CA61A6DF259835580008926E /* Xcode.swift */,
CA25192925A9644800F08414 /* XcodeInstallState.swift */,
CA11E7B92598476C00D2EE1C /* XcodeCommands.swift */,
E87DD6EA25D053FA00D86808 /* Progress+.swift */,
);
path = Backend;
sourceTree = "<group>";
@ -444,6 +446,7 @@
CABFAA1A2592F7D900380FEE /* Frontend */ = {
isa = PBXGroup;
children = (
E8E98A9425D863B100EC89A0 /* InfoPane */,
63EAA4E9259944340046AB8F /* Common */,
CA9FF8552595082000E47BAF /* About */,
CAFE4AAA25B7D29B0064FE51 /* Preferences */,
@ -547,6 +550,15 @@
path = Preferences;
sourceTree = "<group>";
};
E8E98A9425D863B100EC89A0 /* InfoPane */ = {
isa = PBXGroup;
children = (
CAFBDC67259A308B003DCC5A /* InfoPane.swift */,
E8E98A9525D863D700EC89A0 /* InstallationStepDetailView.swift */,
);
path = InfoPane;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@ -757,6 +769,7 @@
CABFA9BD2592EEEA00380FEE /* Environment.swift in Sources */,
CABFA9C32592EEEA00380FEE /* Downloads.swift in Sources */,
CAC281DA259F985100B8AB0B /* InstallationStep.swift in Sources */,
E8E98A9625D863D700EC89A0 /* InstallationStepDetailView.swift in Sources */,
CA378F992466567600A58CE0 /* AppState.swift in Sources */,
CAD2E7A42449574E00113D76 /* XcodeListView.swift in Sources */,
CAA1CB45255A5B60003FD669 /* SignIn2FAView.swift in Sources */,
@ -768,7 +781,6 @@
CAE4247F259A666100B8B246 /* MainWindow.swift in Sources */,
CA452BB0259FD9770072DFA4 /* ProgressIndicator.swift in Sources */,
CAFE4AB425B7D3AF0064FE51 /* AdvancedPreferencePane.swift in Sources */,
CAC281C8259F97E100B8AB0B /* InstallationStepView.swift in Sources */,
CA9FF84E2595079F00E47BAF /* ScrollingTextView.swift in Sources */,
CABFA9C12592EEEA00380FEE /* Version+.swift in Sources */,
CA9FF8522595080100E47BAF /* AcknowledgementsView.swift in Sources */,
@ -782,6 +794,7 @@
CA73510D257BFCEF00EA9CF8 /* NSAttributedString+.swift in Sources */,
CAFBDB952598FE96003DCC5A /* FocusedValues.swift in Sources */,
CAC9F92D25BCDA4400B4965F /* HelperInstallState.swift in Sources */,
E87DD6EB25D053FA00D86808 /* Progress+.swift in Sources */,
CAC281CD259F97FA00B8AB0B /* ObservingProgressIndicator.swift in Sources */,
CABFA9C22592EEEA00380FEE /* Publisher+Resumable.swift in Sources */,
CAFBDC68259A308B003DCC5A /* InfoPane.swift in Sources */,
@ -797,6 +810,7 @@
CA9FF87B2595293E00E47BAF /* DataSource.swift in Sources */,
CABFA9C92592EEEA00380FEE /* URLRequest+Apple.swift in Sources */,
CABFAA432593104F00380FEE /* AboutView.swift in Sources */,
E8E98A9025D8631800EC89A0 /* InstallationStepRowView.swift in Sources */,
CABFA9CC2592EEEA00380FEE /* Path+.swift in Sources */,
CAD2E7A22449574E00113D76 /* XcodesApp.swift in Sources */,
63EAA4EB259944450046AB8F /* ProgressButton.swift in Sources */,

View file

@ -45,6 +45,7 @@ public struct Shell {
"--stop-with-process=\(ProcessInfo.processInfo.processIdentifier)",
"--dir=\(destination.parent.string)",
"--out=\(destination.basename())",
"--human-readable=false", // sets the output to use bytes instead of formatting
url.absoluteString,
]
let stdOutPipe = Pipe()
@ -52,8 +53,10 @@ public struct Shell {
let stdErrPipe = Pipe()
process.standardError = stdErrPipe
var progress = Progress(totalUnitCount: 100)
var progress = Progress()
progress.kind = .file
progress.fileOperationKind = .downloading
let observer = NotificationCenter.default.addObserver(
forName: .NSFileHandleDataAvailable,
object: nil,
@ -68,16 +71,8 @@ public struct Shell {
defer { handle.waitForDataInBackgroundAndNotify() }
let string = String(decoding: handle.availableData, as: UTF8.self)
let regex = try! NSRegularExpression(pattern: #"((?<percent>\d+)%\))"#)
let range = NSRange(location: 0, length: string.utf16.count)
guard
let match = regex.firstMatch(in: string, options: [], range: range),
let matchRange = Range(match.range(withName: "percent"), in: string),
let percentCompleted = Int64(string[matchRange])
else { return }
progress.completedUnitCount = percentCompleted
progress.updateFromAria2(string: string)
}
stdOutPipe.fileHandleForReading.waitForDataInBackgroundAndNotify()

View file

@ -0,0 +1,71 @@
import os.log
import Foundation
extension Progress {
func updateFromAria2(string: String) {
let range = NSRange(location: 0, length: string.utf16.count)
// MARK: Total Downloaded
let regexTotalDownloaded = try! NSRegularExpression(pattern: #"(?<= )(.*)(?=\/)"#)
if let match = regexTotalDownloaded.firstMatch(in: string, options: [], range: range),
let matchRange = Range(match.range(at: 0), in: string),
let totalDownloaded = Int(string[matchRange].replacingOccurrences(of: "B", with: "")) {
self.completedUnitCount = Int64(totalDownloaded)
}
// MARK: Filesize
let regexTotalFileSize = try! NSRegularExpression(pattern: #"(?<=/)(.*)(?=\()"#)
if let match = regexTotalFileSize.firstMatch(in: string, options: [], range: range),
let matchRange = Range(match.range(at: 0), in: string),
let totalFileSize = Int(string[matchRange].replacingOccurrences(of: "B", with: "")) {
if totalFileSize > 0 {
self.totalUnitCount = Int64(totalFileSize)
}
}
// MARK: PERCENT DOWNLOADED
// Since we get fractionCompleted from completedUnitCount + totalUnitCount, no need to process
// let regexPercent = try! NSRegularExpression(pattern: #"((?<percent>\d+)%\))"#)
// MARK: Speed
let regexSpeed = try! NSRegularExpression(pattern: #"(?<=DL:)(.*)(?= )"#)
if let match = regexSpeed.firstMatch(in: string, options: [], range: range),
let matchRange = Range(match.range(at: 0), in: string),
let speed = Int(string[matchRange].replacingOccurrences(of: "B", with: "")) {
self.throughput = speed
} else {
Logger.appState.debug("Could not parse throughput from aria2 download output")
}
// MARK: Estimated Time Remaining
let regexETA = try! NSRegularExpression(pattern: #"(?<=ETA:)(?<hours>\d*h)?(?<minutes>\d*m)?(?<seconds>\d*s)?"#)
if let match = regexETA.firstMatch(in: string, options: [], range: range) {
var seconds: Int = 0
if let matchRange = Range(match.range(withName: "hours"), in: string),
let hours = Int(string[matchRange].replacingOccurrences(of: "h", with: "")) {
seconds += (hours * 60 * 60)
}
if let matchRange = Range(match.range(withName: "minutes"), in: string),
let minutes = Int(string[matchRange].replacingOccurrences(of: "m", with: "")) {
seconds += (minutes * 60)
}
if let matchRange = Range(match.range(withName: "seconds"), in: string),
let second = Int(string[matchRange].replacingOccurrences(of: "s", with: "")) {
seconds += (second)
}
self.estimatedTimeRemaining = TimeInterval(seconds)
}
}
}

View file

@ -8,16 +8,19 @@ import SwiftUI
public struct ObservingProgressIndicator: View {
let controlSize: NSControl.ControlSize
let style: NSProgressIndicator.Style
let showsAdditionalDescription: Bool
@StateObject private var progress: ProgressWrapper
public init(
_ progress: Progress,
controlSize: NSControl.ControlSize,
style: NSProgressIndicator.Style
style: NSProgressIndicator.Style,
showsAdditionalDescription: Bool = false
) {
_progress = StateObject(wrappedValue: ProgressWrapper(progress: progress))
self.controlSize = controlSize
self.style = style
self.showsAdditionalDescription = showsAdditionalDescription
}
class ProgressWrapper: ObservableObject {
@ -26,23 +29,31 @@ public struct ObservingProgressIndicator: View {
init(progress: Progress) {
self.progress = progress
cancellable = progress
.publisher(for: \.fractionCompleted)
.receive(on: RunLoop.main)
cancellable = progress.publisher(for: \.fractionCompleted)
.combineLatest(progress.publisher(for: \.localizedAdditionalDescription))
.throttle(for: 1.0, scheduler: DispatchQueue.main, latest: true)
.sink { [weak self] _ in self?.objectWillChange.send() }
}
}
public var body: some View {
ProgressIndicator(
minValue: 0.0,
maxValue: 1.0,
doubleValue: progress.progress.fractionCompleted,
controlSize: controlSize,
isIndeterminate: progress.progress.isIndeterminate,
style: style
)
.help("Downloading: \(Int((progress.progress.fractionCompleted * 100)))% complete")
VStack(alignment: .leading, spacing: 0) {
ProgressIndicator(
minValue: 0.0,
maxValue: 1.0,
doubleValue: progress.progress.fractionCompleted,
controlSize: controlSize,
isIndeterminate: progress.progress.isIndeterminate,
style: style
)
.help("Downloading: \(Int((progress.progress.fractionCompleted * 100)))% complete")
if showsAdditionalDescription, progress.progress.localizedAdditionalDescription.isEmpty == false {
Text(progress.progress.localizedAdditionalDescription)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
}
}
@ -57,6 +68,20 @@ struct ObservingProgressBar_Previews: PreviewProvider {
controlSize: .small,
style: .spinning
)
ObservingProgressIndicator(
configure(Progress()) {
$0.kind = .file
$0.fileOperationKind = .downloading
$0.estimatedTimeRemaining = 123
$0.totalUnitCount = 11944848484
$0.completedUnitCount = 848444920
$0.throughput = 9211681
},
controlSize: .regular,
style: .bar,
showsAdditionalDescription: true
)
}
.previewLayout(.sizeThatFits)
}

View file

@ -13,51 +13,53 @@ struct InfoPane: View {
var body: some View {
if let xcode = appState.allXcodes.first(where: { $0.id == selectedXcodeID }) {
ScrollView {
VStack(spacing: 16) {
VStack(alignment: .leading, spacing: 16) {
icon(for: xcode)
.frame(maxWidth: .infinity, alignment: .center)
Text(verbatim: "Xcode \(xcode.description) \(xcode.version.buildMetadataIdentifiersDisplay)")
.font(.title)
VStack(alignment: .leading) {
Text(verbatim: "Xcode \(xcode.description) \(xcode.version.buildMetadataIdentifiersDisplay)")
.font(.title)
.frame(maxWidth: .infinity, alignment: .leading)
switch xcode.installState {
case .notInstalled:
InstallButton(xcode: xcode)
downloadFileSize(for: xcode)
case .installing(let installationStep):
InstallationStepDetailView(installationStep: installationStep)
.fixedSize(horizontal: false, vertical: true)
CancelInstallButton(xcode: xcode)
case let .installed(path):
HStack {
Text(path.string)
Button(action: { appState.reveal(id: xcode.id) }) {
Image(systemName: "arrow.right.circle.fill")
}
.buttonStyle(PlainButtonStyle())
.help("Reveal in Finder")
}
switch xcode.installState {
case .notInstalled:
InstallButton(xcode: xcode)
case .installing:
CancelInstallButton(xcode: xcode)
case let .installed(path):
HStack {
Text(path.string)
Button(action: { appState.reveal(id: xcode.id) }) {
Image(systemName: "arrow.right.circle.fill")
}
.buttonStyle(PlainButtonStyle())
.help("Reveal in Finder")
}
HStack {
SelectButton(xcode: xcode)
.disabled(xcode.selected)
.help("Selected")
HStack {
SelectButton(xcode: xcode)
.disabled(xcode.selected)
.help("Selected")
OpenButton(xcode: xcode)
.help("Open")
Spacer()
UninstallButton(xcode: xcode)
}
OpenButton(xcode: xcode)
.help("Open")
Spacer()
UninstallButton(xcode: xcode)
}
}
Divider()
releaseNotes(for: xcode)
identicalBuilds(for: xcode)
compatibility(for: xcode)
sdks(for: xcode)
compilers(for: xcode)
downloadFileSize(for: xcode)
Group{
releaseNotes(for: xcode)
identicalBuilds(for: xcode)
compatibility(for: xcode)
sdks(for: xcode)
compilers(for: xcode)
}
Spacer()
}
@ -210,7 +212,6 @@ struct InfoPane: View {
}
}
@ViewBuilder
private var empty: some View {
Text("No Xcode Selected")
@ -252,7 +253,7 @@ struct InfoPane_Previews: PreviewProvider {
]
})
.previewDisplayName("Populated, Installed, Selected")
InfoPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 0))
.environmentObject(configure(AppState()) {
$0.allXcodes = [
@ -278,7 +279,7 @@ struct InfoPane_Previews: PreviewProvider {
]
})
.previewDisplayName("Populated, Installed, Unselected")
InfoPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 0))
.environmentObject(configure(AppState()) {
$0.allXcodes = [
@ -304,7 +305,7 @@ struct InfoPane_Previews: PreviewProvider {
]
})
.previewDisplayName("Populated, Uninstalled")
InfoPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 1, buildMetadataIdentifiers: ["1234A"]))
.environmentObject(configure(AppState()) {
$0.allXcodes = [
@ -318,6 +319,20 @@ struct InfoPane_Previews: PreviewProvider {
]
})
.previewDisplayName("Basic, installed")
InfoPane(selectedXcodeID: Version(major: 12, minor: 3, patch: 1, buildMetadataIdentifiers: ["1234A"]))
.environmentObject(configure(AppState()) {
$0.allXcodes = [
.init(
version: Version(major: 12, minor: 3, patch: 1, buildMetadataIdentifiers: ["1234A"]),
installState: .installing(.downloading(progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40; $0.throughput = 232323232; $0.fileCompletedCount = 2323004; $0.fileTotalCount = 1193939393 })),
selected: false,
icon: nil,
sdks: nil,
compilers: nil)
]
})
.previewDisplayName("Basic, installing")
InfoPane(selectedXcodeID: nil)
.environmentObject(configure(AppState()) {

View file

@ -0,0 +1,48 @@
import SwiftUI
struct InstallationStepDetailView: View {
let installationStep: InstallationStep
var body: some View {
VStack(alignment: .leading, spacing: 0) {
Text("Step \(installationStep.stepNumber) of \(installationStep.stepCount): \(installationStep.message)")
switch installationStep {
case let .downloading(progress):
ObservingProgressIndicator(
progress,
controlSize: .regular,
style: .bar,
showsAdditionalDescription: true
)
case .unarchiving, .moving, .trashingArchive, .checkingSecurity, .finishing:
ProgressView()
.scaleEffect(0.5)
}
}
}
}
struct InstallDetailView_Previews: PreviewProvider {
static var previews: some View {
Group {
InstallationStepDetailView(
installationStep: .downloading(
progress: configure(Progress()) {
$0.kind = .file
$0.fileOperationKind = .downloading
$0.estimatedTimeRemaining = 123
$0.totalUnitCount = 11944848484
$0.completedUnitCount = 848444920
$0.throughput = 9211681
}
)
)
InstallationStepDetailView(
installationStep: .unarchiving
)
}
}
}

View file

@ -1,6 +1,6 @@
import SwiftUI
struct InstallationStepView: View {
struct InstallationStepRowView: View {
let installationStep: InstallationStep
let highlighted: Bool
let cancel: () -> Void
@ -42,7 +42,7 @@ struct InstallView_Previews: PreviewProvider {
Group {
ForEach(ColorScheme.allCases, id: \.self) { colorScheme in
Group {
InstallationStepView(
InstallationStepRowView(
installationStep: .downloading(
progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 }
),
@ -50,31 +50,31 @@ struct InstallView_Previews: PreviewProvider {
cancel: {}
)
InstallationStepView(
InstallationStepRowView(
installationStep: .unarchiving,
highlighted: false,
cancel: {}
)
InstallationStepView(
InstallationStepRowView(
installationStep: .moving(destination: "/Applications"),
highlighted: false,
cancel: {}
)
InstallationStepView(
InstallationStepRowView(
installationStep: .trashingArchive,
highlighted: false,
cancel: {}
)
InstallationStepView(
InstallationStepRowView(
installationStep: .checkingSecurity,
highlighted: false,
cancel: {}
)
InstallationStepView(
InstallationStepRowView(
installationStep: .finishing,
highlighted: false,
cancel: {}
@ -87,7 +87,7 @@ struct InstallView_Previews: PreviewProvider {
ForEach(ColorScheme.allCases, id: \.self) { colorScheme in
Group {
InstallationStepView(
InstallationStepRowView(
installationStep: .downloading(
progress: configure(Progress(totalUnitCount: 100)) { $0.completedUnitCount = 40 }
),

View file

@ -109,7 +109,7 @@ struct XcodeListViewRow: View {
.buttonStyle(AppStoreButtonStyle(primary: false, highlighted: selected))
.help("Install this version")
case let .installing(installationStep):
InstallationStepView(
InstallationStepRowView(
installationStep: installationStep,
highlighted: selected,
cancel: { appState.xcodeBeingConfirmedForInstallCancellation = xcode }