2020-02-19 03:36:41 +00:00
import SwiftUI
2020-02-19 04:52:00 +00:00
import SecretKit
2022-01-02 00:43:29 +00:00
import SecureEnclaveSecretKit
import SmartCardSecretKit
2020-03-22 01:43:26 +00:00
import Brief
2020-02-19 04:52:00 +00:00
2020-03-15 21:36:07 +00:00
struct ContentView < UpdaterType : UpdaterProtocol , AgentStatusCheckerType : AgentStatusCheckerProtocol > : View {
2020-09-22 06:12:50 +00:00
@ Binding var showingCreation : Bool
@ Binding var runningSetup : Bool
@ Binding var hasRunSetup : Bool
2022-12-22 22:20:03 +00:00
@ State var showingAgentInfo = false
@ Environment ( \ . colorScheme ) var colorScheme
2020-09-22 06:12:50 +00:00
@ EnvironmentObject private var storeList : SecretStoreList
@ EnvironmentObject private var updater : UpdaterType
@ EnvironmentObject private var agentStatusChecker : AgentStatusCheckerType
@ State private var selectedUpdate : Release ?
@ State private var showingAppPathNotice = false
2020-02-19 03:36:41 +00:00
var body : some View {
2020-03-15 08:01:40 +00:00
VStack {
2020-03-21 03:20:20 +00:00
if storeList . anyAvailable {
2020-09-22 06:12:50 +00:00
StoreListView ( showingCreation : $ showingCreation )
2020-03-21 03:20:20 +00:00
} else {
NoStoresView ( )
}
2020-09-22 06:12:50 +00:00
}
. frame ( minWidth : 640 , minHeight : 320 )
. toolbar {
2022-12-22 22:20:03 +00:00
toolbarItem ( updateNoticeView , id : " update " )
toolbarItem ( runningOrRunSetupView , id : " setup " )
toolbarItem ( appPathNoticeView , id : " appPath " )
toolbarItem ( newItemView , id : " new " )
2020-09-22 06:12:50 +00:00
}
2022-01-31 07:44:09 +00:00
. sheet ( isPresented : $ runningSetup ) {
SetupView ( visible : $ runningSetup , setupComplete : $ hasRunSetup )
}
2020-02-19 03:36:41 +00:00
}
2020-03-15 08:01:40 +00:00
2020-09-22 06:12:50 +00:00
}
extension ContentView {
2022-12-22 22:20:03 +00:00
func toolbarItem ( _ view : some View , id : String ) -> ToolbarItem < String , some View > {
ToolbarItem ( id : id ) { view }
}
var needsSetup : Bool {
( runningSetup || ! hasRunSetup || ! agentStatusChecker . running ) && ! agentStatusChecker . developmentBuild
}
// / I t e m e i t h e r s h o w i n g a " e v e r y t h i n g ' s g o o d , h e r e ' s m o r e i n f o " o r " s o m e t h i n g ' s w r o n g , r e - r u n s e t u p " m e s s a g e
// / T h e s e t w o a r e m u t u a l l y e x c l u s i v e
@ ViewBuilder
var runningOrRunSetupView : some View {
if needsSetup {
setupNoticeView
} else {
runningNoticeView
2020-09-22 06:12:50 +00:00
}
2022-12-22 22:20:03 +00:00
}
var updateNoticeContent : ( String , Color ) ? {
guard let update = updater . update else { return nil }
2020-03-15 08:01:40 +00:00
if update . critical {
2022-12-22 22:20:03 +00:00
return ( " Critical Security Update Required " , . red )
2020-03-15 08:01:40 +00:00
} else {
2021-12-31 22:59:12 +00:00
if updater . testBuild {
2022-12-22 22:20:03 +00:00
return ( " Test Build " , . blue )
2021-12-31 22:59:12 +00:00
} else {
2022-12-22 22:20:03 +00:00
return ( " Update Available " , . orange )
2021-12-31 22:59:12 +00:00
}
2020-09-22 06:12:50 +00:00
}
2020-03-15 08:01:40 +00:00
}
2022-12-22 22:20:03 +00:00
@ ViewBuilder
var updateNoticeView : some View {
if let update = updater . update , let ( text , color ) = updateNoticeContent {
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 )
}
2020-09-22 06:12:50 +00:00
}
2022-12-22 22:20:03 +00:00
}
2020-11-12 00:45:39 +00:00
2022-12-22 22:20:03 +00:00
@ ViewBuilder
var newItemView : some View {
if storeList . modifiableStore ? . isAvailable ? ? false {
Button ( action : {
showingCreation = true
} , label : {
Image ( systemName : " plus " )
} )
. sheet ( isPresented : $ showingCreation ) {
if let modifiable = storeList . modifiableStore {
CreateSecretView ( store : modifiable , showing : $ showingCreation )
}
}
2020-03-15 21:36:07 +00:00
}
}
2022-12-22 22:20:03 +00:00
@ 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 " )
2020-09-22 06:12:50 +00:00
}
2022-12-22 22:20:03 +00:00
}
. font ( . headline )
. foregroundColor ( . white )
} )
. background ( Color . orange )
. cornerRadius ( 5 )
2020-03-04 07:14:38 +00:00
}
2020-03-15 21:03:57 +00:00
2022-12-22 22:20:03 +00:00
@ ViewBuilder
var runningNoticeView : some View {
Button ( action : {
showingAgentInfo = true
} , label : {
HStack {
Text ( " Agent is Running " )
2020-09-22 06:12:50 +00:00
. font ( . headline )
2022-12-22 22:20:03 +00:00
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 " )
2022-01-31 07:44:09 +00:00
}
2022-12-22 22:20:03 +00:00
. 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 ( )
}
2020-03-15 21:03:57 +00:00
}
}
2020-03-11 07:02:17 +00:00
2020-03-11 08:53:31 +00:00
}
2020-03-12 06:07:22 +00:00
#if DEBUG
2020-03-11 07:02:17 +00:00
struct ContentView_Previews : PreviewProvider {
2020-09-22 06:12:50 +00:00
private static let storeList : SecretStoreList = {
let list = SecretStoreList ( )
list . add ( store : SecureEnclave . Store ( ) )
list . add ( store : SmartCard . Store ( ) )
return list
} ( )
private static let agentStatusChecker = AgentStatusChecker ( )
private static let justUpdatedChecker = JustUpdatedChecker ( )
@ State var hasRunSetup = false
@ State private var showingSetup = false
@ State private var showingCreation = false
2020-03-11 07:02:17 +00:00
static var previews : some View {
Group {
2020-09-22 06:12:50 +00:00
// E m p t y o n m o d i f i a b l e a n d n o n m o d i f i a b l e
ContentView < PreviewUpdater , AgentStatusChecker > ( showingCreation : . constant ( false ) , runningSetup : . constant ( false ) , hasRunSetup : . constant ( true ) )
. environmentObject ( Preview . storeList ( stores : [ Preview . Store ( numberOfRandomSecrets : 0 ) ] , modifiableStores : [ Preview . StoreModifiable ( numberOfRandomSecrets : 0 ) ] ) )
. environmentObject ( PreviewUpdater ( ) )
. environmentObject ( agentStatusChecker )
// 5 i t e m s o n m o d i f i a b l e a n d n o n m o d i f i a b l e
ContentView < PreviewUpdater , AgentStatusChecker > ( showingCreation : . constant ( false ) , runningSetup : . constant ( false ) , hasRunSetup : . constant ( true ) )
. environmentObject ( Preview . storeList ( stores : [ Preview . Store ( ) ] , modifiableStores : [ Preview . StoreModifiable ( ) ] ) )
. environmentObject ( PreviewUpdater ( ) )
. environmentObject ( agentStatusChecker )
2020-03-11 07:02:17 +00:00
}
2020-09-22 06:12:50 +00:00
. environmentObject ( agentStatusChecker )
2020-03-11 07:02:17 +00:00
}
}
2020-03-12 06:07:22 +00:00
#endif
2022-12-22 22:20:03 +00:00
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
// . b u t t o n S t y l e ( . b o r d e r e d )
. padding ( )
. background ( colorScheme = = . light ? lightColor : darkColor )
. foregroundColor ( . white )
}
}