Switch toolbar items to viewbuilders (#434)
* Switch toolbar items to viewbuilders * Toolbar button style
This commit is contained in:
parent
f43571baa3
commit
8679ca3da0
|
@ -31,6 +31,7 @@
|
||||||
50617D8A23FCE48E0099B055 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8923FCE48E0099B055 /* Preview Assets.xcassets */; };
|
50617D8A23FCE48E0099B055 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 50617D8923FCE48E0099B055 /* Preview Assets.xcassets */; };
|
||||||
50617D9923FCE48E0099B055 /* SecretiveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D9823FCE48E0099B055 /* SecretiveTests.swift */; };
|
50617D9923FCE48E0099B055 /* SecretiveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617D9823FCE48E0099B055 /* SecretiveTests.swift */; };
|
||||||
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617DD123FCEFA90099B055 /* PreviewStore.swift */; };
|
50617DD223FCEFA90099B055 /* PreviewStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50617DD123FCEFA90099B055 /* PreviewStore.swift */; };
|
||||||
|
5065E313295517C500E16645 /* ToolbarButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5065E312295517C500E16645 /* ToolbarButtonStyle.swift */; };
|
||||||
5066A6C22516F303004B5A36 /* SetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6C12516F303004B5A36 /* SetupView.swift */; };
|
5066A6C22516F303004B5A36 /* SetupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6C12516F303004B5A36 /* SetupView.swift */; };
|
||||||
5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6C72516FE6E004B5A36 /* CopyableView.swift */; };
|
5066A6C82516FE6E004B5A36 /* CopyableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6C72516FE6E004B5A36 /* CopyableView.swift */; };
|
||||||
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */; };
|
5066A6F7251829B1004B5A36 /* ShellConfigurationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */; };
|
||||||
|
@ -123,6 +124,7 @@
|
||||||
50617D9823FCE48E0099B055 /* SecretiveTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretiveTests.swift; sourceTree = "<group>"; };
|
50617D9823FCE48E0099B055 /* SecretiveTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecretiveTests.swift; sourceTree = "<group>"; };
|
||||||
50617D9A23FCE48E0099B055 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
50617D9A23FCE48E0099B055 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
50617DD123FCEFA90099B055 /* PreviewStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewStore.swift; sourceTree = "<group>"; };
|
50617DD123FCEFA90099B055 /* PreviewStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreviewStore.swift; sourceTree = "<group>"; };
|
||||||
|
5065E312295517C500E16645 /* ToolbarButtonStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolbarButtonStyle.swift; sourceTree = "<group>"; };
|
||||||
5066A6C12516F303004B5A36 /* SetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupView.swift; sourceTree = "<group>"; };
|
5066A6C12516F303004B5A36 /* SetupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetupView.swift; sourceTree = "<group>"; };
|
||||||
5066A6C72516FE6E004B5A36 /* CopyableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyableView.swift; sourceTree = "<group>"; };
|
5066A6C72516FE6E004B5A36 /* CopyableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CopyableView.swift; sourceTree = "<group>"; };
|
||||||
5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellConfigurationController.swift; sourceTree = "<group>"; };
|
5066A6F6251829B1004B5A36 /* ShellConfigurationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShellConfigurationController.swift; sourceTree = "<group>"; };
|
||||||
|
@ -264,6 +266,7 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
50617D8423FCE48E0099B055 /* ContentView.swift */,
|
50617D8423FCE48E0099B055 /* ContentView.swift */,
|
||||||
|
5065E312295517C500E16645 /* ToolbarButtonStyle.swift */,
|
||||||
5079BA0E250F29BF00EA86F4 /* StoreListView.swift */,
|
5079BA0E250F29BF00EA86F4 /* StoreListView.swift */,
|
||||||
50153E21250DECA300525160 /* SecretListItemView.swift */,
|
50153E21250DECA300525160 /* SecretListItemView.swift */,
|
||||||
50C385A42407A76D00AF2719 /* SecretDetailView.swift */,
|
50C385A42407A76D00AF2719 /* SecretDetailView.swift */,
|
||||||
|
@ -475,6 +478,7 @@
|
||||||
2C4A9D2F2636FFD3008CC8E2 /* RenameSecretView.swift in Sources */,
|
2C4A9D2F2636FFD3008CC8E2 /* RenameSecretView.swift in Sources */,
|
||||||
5091D2BC25183B830049FD9B /* ApplicationDirectoryController.swift in Sources */,
|
5091D2BC25183B830049FD9B /* ApplicationDirectoryController.swift in Sources */,
|
||||||
5066A6C22516F303004B5A36 /* SetupView.swift in Sources */,
|
5066A6C22516F303004B5A36 /* SetupView.swift in Sources */,
|
||||||
|
5065E313295517C500E16645 /* ToolbarButtonStyle.swift in Sources */,
|
||||||
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */,
|
50617D8523FCE48E0099B055 /* ContentView.swift in Sources */,
|
||||||
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */,
|
50571E0324393C2600F76F6C /* JustUpdatedChecker.swift in Sources */,
|
||||||
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
|
5079BA0F250F29BF00EA86F4 /* StoreListView.swift in Sources */,
|
||||||
|
|
|
@ -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,141 @@ 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)
|
.buttonStyle(ToolbarButtonStyle(color: color))
|
||||||
.cornerRadius(5)
|
.popover(item: $selectedUpdate, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) { update in
|
||||||
.popover(isPresented: $showingAppPathNotice, attachmentAnchor: .point(.bottom), arrowEdge: .bottom) {
|
UpdateDetailView(update: update)
|
||||||
VStack {
|
}
|
||||||
Image(systemName: "exclamationmark.triangle")
|
}
|
||||||
.resizable()
|
}
|
||||||
.aspectRatio(contentMode: .fit)
|
|
||||||
.frame(width: 64)
|
@ViewBuilder
|
||||||
Text("Secretive needs to be in your Applications folder to work properly. Please move it and relaunch.")
|
var newItemView: some View {
|
||||||
.frame(maxWidth: 300)
|
if storeList.modifiableStore?.isAvailable ?? false {
|
||||||
}
|
Button(action: {
|
||||||
.padding()
|
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)
|
||||||
|
})
|
||||||
|
.buttonStyle(ToolbarButtonStyle(color: .orange))
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder
|
||||||
|
var runningNoticeView: some View {
|
||||||
|
Button(action: {
|
||||||
|
showingAgentInfo = true
|
||||||
|
}, label: {
|
||||||
|
HStack {
|
||||||
|
Text("Agent is Running")
|
||||||
|
.font(.headline)
|
||||||
|
.foregroundColor(colorScheme == .light ? Color(white: 0.3) : .white)
|
||||||
|
Circle()
|
||||||
|
.frame(width: 10, height: 10)
|
||||||
|
.foregroundColor(Color.green)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.buttonStyle(ToolbarButtonStyle(lightColor: .black.opacity(0.05), darkColor: .white.opacity(0.05)))
|
||||||
|
.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)
|
||||||
|
})
|
||||||
|
.buttonStyle(ToolbarButtonStyle(color: .orange))
|
||||||
|
.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 +220,4 @@ struct ContentView_Previews: PreviewProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ToolbarButtonStyle: ButtonStyle {
|
||||||
|
|
||||||
|
private let lightColor: Color
|
||||||
|
private let darkColor: Color
|
||||||
|
@Environment(\.colorScheme) var colorScheme
|
||||||
|
@State var hovering = false
|
||||||
|
|
||||||
|
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
|
||||||
|
.padding(EdgeInsets(top: 6, leading: 8, bottom: 6, trailing: 8))
|
||||||
|
.background(colorScheme == .light ? lightColor : darkColor)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 5))
|
||||||
|
.overlay(
|
||||||
|
RoundedRectangle(cornerRadius: 5)
|
||||||
|
.stroke(colorScheme == .light ? .black.opacity(0.15) : .white.opacity(0.15), lineWidth: 1)
|
||||||
|
.background(hovering ? (colorScheme == .light ? .black.opacity(0.1) : .white.opacity(0.05)) : Color.clear)
|
||||||
|
)
|
||||||
|
.onHover { hovering in
|
||||||
|
withAnimation {
|
||||||
|
self.hovering = hovering
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue