Switch toolbar items to viewbuilders
This commit is contained in:
parent
acdf0baf3a
commit
d429d090cf
|
@ -9,6 +9,8 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
|
||||||
@Binding var showingCreation: Bool
|
@Binding var showingCreation: Bool
|
||||||
@Binding var runningSetup: Bool
|
@Binding var runningSetup: Bool
|
||||||
@Binding var hasRunSetup: Bool
|
@Binding var hasRunSetup: Bool
|
||||||
|
@State var showingAgentInfo = false
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
|
||||||
@EnvironmentObject private var storeList: SecretStoreList
|
@EnvironmentObject private var storeList: SecretStoreList
|
||||||
@EnvironmentObject private var updater: UpdaterType
|
@EnvironmentObject private var updater: UpdaterType
|
||||||
|
@ -27,10 +29,10 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
|
||||||
}
|
}
|
||||||
.frame(minWidth: 640, minHeight: 320)
|
.frame(minWidth: 640, minHeight: 320)
|
||||||
.toolbar {
|
.toolbar {
|
||||||
updateNotice
|
toolbarItem(updateNoticeView, id: "update")
|
||||||
setupNotice
|
toolbarItem(runningOrRunSetupView, id: "setup")
|
||||||
appPathNotice
|
toolbarItem(appPathNoticeView, id: "appPath")
|
||||||
newItem
|
toolbarItem(newItemView, id: "new")
|
||||||
}
|
}
|
||||||
.sheet(isPresented: $runningSetup) {
|
.sheet(isPresented: $runningSetup) {
|
||||||
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
|
SetupView(visible: $runningSetup, setupComplete: $hasRunSetup)
|
||||||
|
@ -41,121 +43,144 @@ struct ContentView<UpdaterType: UpdaterProtocol, AgentStatusCheckerType: AgentSt
|
||||||
|
|
||||||
extension ContentView {
|
extension ContentView {
|
||||||
|
|
||||||
var updateNotice: ToolbarItem<Void, AnyView> {
|
|
||||||
guard let update = updater.update else {
|
func toolbarItem(_ view: some View, id: String) -> ToolbarItem<String, some View> {
|
||||||
return ToolbarItem { AnyView(EmptyView()) }
|
ToolbarItem(id: id) { view }
|
||||||
|
}
|
||||||
|
|
||||||
|
var needsSetup: Bool {
|
||||||
|
(runningSetup || !hasRunSetup || !agentStatusChecker.running) && !agentStatusChecker.developmentBuild
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Item either showing a "everything's good, here's more info" or "something's wrong, re-run setup" message
|
||||||
|
/// These two are mutually exclusive
|
||||||
|
@ViewBuilder
|
||||||
|
var runningOrRunSetupView: some View {
|
||||||
|
if needsSetup {
|
||||||
|
setupNoticeView
|
||||||
|
} else {
|
||||||
|
runningNoticeView
|
||||||
}
|
}
|
||||||
let color: Color
|
}
|
||||||
let text: String
|
|
||||||
|
var updateNoticeContent: (String, Color)? {
|
||||||
|
guard let update = updater.update else { return nil }
|
||||||
if update.critical {
|
if update.critical {
|
||||||
text = "Critical Security Update Required"
|
return ("Critical Security Update Required", .red)
|
||||||
color = .red
|
|
||||||
} else {
|
} else {
|
||||||
if updater.testBuild {
|
if updater.testBuild {
|
||||||
text = "Test Build"
|
return ("Test Build", .blue)
|
||||||
color = .blue
|
|
||||||
} else {
|
} else {
|
||||||
text = "Update Available"
|
return ("Update Available", .orange)
|
||||||
color = .orange
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ToolbarItem {
|
|
||||||
AnyView(
|
|
||||||
Button(action: {
|
|
||||||
selectedUpdate = update
|
|
||||||
}, label: {
|
|
||||||
Text(text)
|
|
||||||
.font(.headline)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
})
|
|
||||||
.background(color)
|
|
||||||
.cornerRadius(5)
|
|
||||||
.popover(item: $selectedUpdate, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) { update in
|
|
||||||
UpdateDetailView(update: update)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var newItem: ToolbarItem<Void, AnyView> {
|
@ViewBuilder
|
||||||
guard storeList.modifiableStore?.isAvailable ?? false else {
|
var updateNoticeView: some View {
|
||||||
return ToolbarItem { AnyView(EmptyView()) }
|
if let update = updater.update, let (text, color) = updateNoticeContent {
|
||||||
}
|
Button(action: {
|
||||||
return ToolbarItem {
|
selectedUpdate = update
|
||||||
AnyView(
|
}, label: {
|
||||||
Button(action: {
|
Text(text)
|
||||||
showingCreation = true
|
|
||||||
}, label: {
|
|
||||||
Image(systemName: "plus")
|
|
||||||
})
|
|
||||||
.sheet(isPresented: $showingCreation) {
|
|
||||||
if let modifiable = storeList.modifiableStore {
|
|
||||||
CreateSecretView(store: modifiable, showing: $showingCreation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var setupNotice: ToolbarItem<Void, AnyView> {
|
|
||||||
return ToolbarItem {
|
|
||||||
AnyView(
|
|
||||||
Group {
|
|
||||||
if (runningSetup || !hasRunSetup || !agentStatusChecker.running) && !agentStatusChecker.developmentBuild {
|
|
||||||
Button(action: {
|
|
||||||
runningSetup = true
|
|
||||||
}, label: {
|
|
||||||
Group {
|
|
||||||
if hasRunSetup && !agentStatusChecker.running {
|
|
||||||
Text("Secret Agent Is Not Running")
|
|
||||||
} else {
|
|
||||||
Text("Setup Secretive")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.font(.headline)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
})
|
|
||||||
.background(Color.orange)
|
|
||||||
.cornerRadius(5)
|
|
||||||
} else {
|
|
||||||
EmptyView()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var appPathNotice: ToolbarItem<Void, AnyView> {
|
|
||||||
let controller = ApplicationDirectoryController()
|
|
||||||
guard !controller.isInApplicationsDirectory else {
|
|
||||||
return ToolbarItem { AnyView(EmptyView()) }
|
|
||||||
}
|
|
||||||
return ToolbarItem {
|
|
||||||
AnyView(
|
|
||||||
Button(action: {
|
|
||||||
showingAppPathNotice = true
|
|
||||||
}, label: {
|
|
||||||
Group {
|
|
||||||
Text("Secretive Is Not in Applications Folder")
|
|
||||||
}
|
|
||||||
.font(.headline)
|
.font(.headline)
|
||||||
.foregroundColor(.white)
|
.foregroundColor(.white)
|
||||||
})
|
})
|
||||||
.background(Color.orange)
|
.background(color)
|
||||||
.cornerRadius(5)
|
.cornerRadius(5)
|
||||||
.popover(isPresented: $showingAppPathNotice, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
.popover(item: $selectedUpdate, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) { update in
|
||||||
VStack {
|
UpdateDetailView(update: update)
|
||||||
Image(systemName: "exclamationmark.triangle")
|
}
|
||||||
.resizable()
|
}
|
||||||
.aspectRatio(contentMode: .fit)
|
}
|
||||||
.frame(width: 64)
|
|
||||||
Text("Secretive needs to be in your Applications folder to work properly. Please move it and relaunch.")
|
@ViewBuilder
|
||||||
.frame(maxWidth: 300)
|
var newItemView: some View {
|
||||||
}
|
if storeList.modifiableStore?.isAvailable ?? false {
|
||||||
.padding()
|
Button(action: {
|
||||||
|
showingCreation = true
|
||||||
|
}, label: {
|
||||||
|
Image(systemName: "plus")
|
||||||
|
})
|
||||||
|
.sheet(isPresented: $showingCreation) {
|
||||||
|
if let modifiable = storeList.modifiableStore {
|
||||||
|
CreateSecretView(store: modifiable, showing: $showingCreation)
|
||||||
}
|
}
|
||||||
)
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var setupNoticeView: some View {
|
||||||
|
Button(action: {
|
||||||
|
runningSetup = true
|
||||||
|
}, label: {
|
||||||
|
Group {
|
||||||
|
if hasRunSetup && !agentStatusChecker.running {
|
||||||
|
Text("Secret Agent Is Not Running")
|
||||||
|
} else {
|
||||||
|
Text("Setup Secretive")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
})
|
||||||
|
.background(Color.orange)
|
||||||
|
.cornerRadius(5)
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var runningNoticeView: some View {
|
||||||
|
Button(action: {
|
||||||
|
showingAgentInfo = true
|
||||||
|
}, label: {
|
||||||
|
HStack {
|
||||||
|
Text("Agent is Running")
|
||||||
|
.font(.headline)
|
||||||
|
Circle()
|
||||||
|
.frame(width: 10, height: 10)
|
||||||
|
.foregroundColor(Color.green)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.background((colorScheme == .dark ? Color.white : Color.black).opacity(0.05))
|
||||||
|
.cornerRadius(5)
|
||||||
|
.popover(isPresented: $showingAgentInfo, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
||||||
|
VStack {
|
||||||
|
Text("SecretAgent is Running")
|
||||||
|
.font(.title)
|
||||||
|
.padding(5)
|
||||||
|
Text("SecretAgent is a process that runs in the background to sign requests, so you don't need to keep Secretive open all the time.\n\n**You can close Secretive, and everything will still keep working.**")
|
||||||
|
.frame(width: 300)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var appPathNoticeView: some View {
|
||||||
|
if !ApplicationDirectoryController().isInApplicationsDirectory {
|
||||||
|
Button(action: {
|
||||||
|
showingAppPathNotice = true
|
||||||
|
}, label: {
|
||||||
|
Group {
|
||||||
|
Text("Secretive Is Not in Applications Folder")
|
||||||
|
}
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
})
|
||||||
|
.background(Color.orange)
|
||||||
|
.cornerRadius(5)
|
||||||
|
.popover(isPresented: $showingAppPathNotice, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
||||||
|
VStack {
|
||||||
|
Image(systemName: "exclamationmark.triangle")
|
||||||
|
.resizable()
|
||||||
|
.aspectRatio(contentMode: .fit)
|
||||||
|
.frame(width: 64)
|
||||||
|
Text("Secretive needs to be in your Applications folder to work properly. Please move it and relaunch.")
|
||||||
|
.frame(maxWidth: 300)
|
||||||
|
}
|
||||||
|
.padding()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,3 +223,28 @@ struct ContentView_Previews: PreviewProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct ToolbarButton: ButtonStyle {
|
||||||
|
|
||||||
|
private let lightColor: Color
|
||||||
|
private let darkColor: Color
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
|
||||||
|
init(color: Color) {
|
||||||
|
self.lightColor = color
|
||||||
|
self.darkColor = color
|
||||||
|
}
|
||||||
|
|
||||||
|
init(lightColor: Color, darkColor: Color) {
|
||||||
|
self.lightColor = lightColor
|
||||||
|
self.darkColor = darkColor
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeBody(configuration: Configuration) -> some View {
|
||||||
|
configuration.label
|
||||||
|
// .buttonStyle(.bordered)
|
||||||
|
.padding()
|
||||||
|
.background(colorScheme == .light ? lightColor : darkColor)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue