-
Notifications
You must be signed in to change notification settings - Fork 3
feat: add coder connect startup progress indicator #161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 11 commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
ccf5f10
feat: add coder connect startup progress messages
ethanndickson d5d249f
log level
ethanndickson a2f3c47
download progress
ethanndickson 065502b
progress gauge
ethanndickson 64ffe17
fix tests
ethanndickson 5177bd0
fixup
ethanndickson 8d3b4a6
scaling
ethanndickson 26eaa88
throttle
ethanndickson 5936021
not being able to sync swift versions strikes again!
ethanndickson 56d7e1f
remove two stages
ethanndickson c2728dd
fixup
ethanndickson 59d3aff
size
ethanndickson File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import SwiftUI | ||
import VPNLib | ||
|
||
struct VPNProgress { | ||
let stage: ProgressStage | ||
let downloadProgress: DownloadProgress? | ||
} | ||
|
||
struct VPNProgressView: View { | ||
let state: VPNServiceState | ||
let progress: VPNProgress | ||
|
||
var body: some View { | ||
VStack { | ||
CircularProgressView(value: value) | ||
// We estimate that the last half takes 8 seconds | ||
// so it doesn't appear stuck | ||
.autoComplete(threshold: 0.5, duration: 8) | ||
Text(progressMessage) | ||
.multilineTextAlignment(.center) | ||
} | ||
.padding() | ||
.foregroundStyle(.secondary) | ||
} | ||
|
||
var progressMessage: String { | ||
"\(progress.stage.description ?? defaultMessage)\(downloadProgressMessage)" | ||
} | ||
|
||
var downloadProgressMessage: String { | ||
progress.downloadProgress.flatMap { "\n\($0.description)" } ?? "" | ||
} | ||
|
||
var defaultMessage: String { | ||
state == .connecting ? "Starting Coder Connect..." : "Stopping Coder Connect..." | ||
} | ||
|
||
var value: Float? { | ||
guard state == .connecting else { | ||
return nil | ||
} | ||
switch progress.stage { | ||
case .initial: | ||
return 0 | ||
case .downloading: | ||
guard let downloadProgress = progress.downloadProgress else { | ||
// We can't make this illegal state unrepresentable because XPC | ||
// doesn't support enums with associated values. | ||
return 0.05 | ||
} | ||
// 40MB if the server doesn't give us the expected size | ||
let totalBytes = downloadProgress.totalBytesToWrite ?? 40_000_000 | ||
let downloadPercent = min(1.0, Float(downloadProgress.totalBytesWritten) / Float(totalBytes)) | ||
return 0.4 * downloadPercent | ||
case .validating: | ||
return 0.43 | ||
case .removingQuarantine: | ||
return 0.46 | ||
case .startingTunnel: | ||
return 0.50 | ||
} | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
80 changes: 80 additions & 0 deletions
80
Coder-Desktop/Coder-Desktop/Views/CircularProgressView.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import SwiftUI | ||
|
||
struct CircularProgressView: View { | ||
let value: Float? | ||
|
||
var strokeWidth: CGFloat = 4 | ||
var diameter: CGFloat = 22 | ||
var primaryColor: Color = .secondary | ||
var backgroundColor: Color = .secondary.opacity(0.3) | ||
|
||
@State private var rotation = 0.0 | ||
@State private var trimAmount: CGFloat = 0.15 | ||
|
||
var autoCompleteThreshold: Float? | ||
var autoCompleteDuration: TimeInterval? | ||
|
||
var body: some View { | ||
ZStack { | ||
// Background circle | ||
Circle() | ||
.stroke(backgroundColor, style: StrokeStyle(lineWidth: strokeWidth, lineCap: .round)) | ||
.frame(width: diameter, height: diameter) | ||
Group { | ||
if let value { | ||
// Determinate gauge | ||
Circle() | ||
.trim(from: 0, to: CGFloat(displayValue(for: value))) | ||
.stroke(primaryColor, style: StrokeStyle(lineWidth: strokeWidth, lineCap: .round)) | ||
.frame(width: diameter, height: diameter) | ||
.rotationEffect(.degrees(-90)) | ||
.animation(autoCompleteAnimation(for: value), value: value) | ||
} else { | ||
// Indeterminate gauge | ||
Circle() | ||
.trim(from: 0, to: trimAmount) | ||
.stroke(primaryColor, style: StrokeStyle(lineWidth: strokeWidth, lineCap: .round)) | ||
.frame(width: diameter, height: diameter) | ||
.rotationEffect(.degrees(rotation)) | ||
} | ||
} | ||
} | ||
.frame(width: diameter + strokeWidth * 2, height: diameter + strokeWidth * 2) | ||
.onAppear { | ||
if value == nil { | ||
withAnimation(.linear(duration: 0.8).repeatForever(autoreverses: false)) { | ||
rotation = 360 | ||
} | ||
} | ||
} | ||
} | ||
|
||
private func displayValue(for value: Float) -> Float { | ||
if let threshold = autoCompleteThreshold, | ||
value >= threshold, value < 1.0 | ||
{ | ||
return 1.0 | ||
} | ||
return value | ||
} | ||
|
||
private func autoCompleteAnimation(for value: Float) -> Animation? { | ||
guard let threshold = autoCompleteThreshold, | ||
let duration = autoCompleteDuration, | ||
value >= threshold, value < 1.0 | ||
else { | ||
return .default | ||
} | ||
|
||
return .easeOut(duration: duration) | ||
} | ||
} | ||
|
||
extension CircularProgressView { | ||
func autoComplete(threshold: Float, duration: TimeInterval) -> CircularProgressView { | ||
var view = self | ||
view.autoCompleteThreshold = threshold | ||
view.autoCompleteDuration = duration | ||
return view | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.