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 23:05:04 +00:00
@ State var showingAgentInfo = false
2022-12-23 04:29:51 +00:00
@ State var activeSecret : AnySecret . ID ?
2022-12-22 23:05:04 +00:00
@ 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 {
2022-12-23 04:29:51 +00:00
StoreListView ( activeSecret : $ activeSecret )
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 23:05:04 +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 23:05:04 +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 23:05:04 +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 23:05:04 +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 23:05:04 +00:00
return ( " Test Build " , . blue )
2021-12-31 22:59:12 +00:00
} else {
2022-12-22 23:05:04 +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 23:05:04 +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 )
} )
. buttonStyle ( ToolbarButtonStyle ( color : color ) )
2023-07-23 21:35:28 +00:00
. popover ( item : $ selectedUpdate , attachmentAnchor : attachmentAnchor , arrowEdge : . bottom ) { update in
2022-12-22 23:05:04 +00:00
UpdateDetailView ( update : update )
}
2020-09-22 06:12:50 +00:00
}
2022-12-22 23:05:04 +00:00
}
2020-11-12 00:45:39 +00:00
2022-12-22 23:05:04 +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 )
2022-12-23 04:29:51 +00:00
. onDisappear {
guard let newest = modifiable . secrets . last ? . id else { return }
activeSecret = newest
}
2022-12-22 23:05:04 +00:00
}
}
2020-03-15 21:36:07 +00:00
}
}
2022-12-22 23:05:04 +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 23:05:04 +00:00
}
. font ( . headline )
. foregroundColor ( . white )
} )
. buttonStyle ( ToolbarButtonStyle ( color : . orange ) )
2020-03-04 07:14:38 +00:00
}
2020-03-15 21:03:57 +00:00
2022-12-22 23:05:04 +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 23:05:04 +00:00
. 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 ) ) )
2023-07-23 21:35:28 +00:00
. popover ( isPresented : $ showingAgentInfo , attachmentAnchor : attachmentAnchor , arrowEdge : . bottom ) {
2022-12-22 23:05:04 +00:00
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 23:05:04 +00:00
. font ( . headline )
. foregroundColor ( . white )
} )
. buttonStyle ( ToolbarButtonStyle ( color : . orange ) )
2023-07-23 21:35:28 +00:00
. popover ( isPresented : $ showingAppPathNotice , attachmentAnchor : attachmentAnchor , arrowEdge : . bottom ) {
2022-12-22 23:05:04 +00:00
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
2023-07-23 21:35:28 +00:00
var attachmentAnchor : PopoverAttachmentAnchor {
// I d e a l l y . p o i n t ( . b o t t o m ) , b u t b r o k e n o n S o n o m a ( F B 1 2 7 2 6 5 0 3 )
. rect ( . bounds )
}
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 23:05:04 +00:00