@@ -10,7 +10,7 @@ struct FileSyncConfig<VPN: VPNService, FS: FileSyncDaemon>: View {
1010@State private var editingSession : FileSyncSession ?
1111
1212@State private var loading : Bool = false
13- @State private var deleteError : DaemonError ?
13+ @State private var actionError : DaemonError ?
1414@State private var isVisible : Bool = false
1515@State private var dontRetry : Bool = false
1616
@@ -50,14 +50,14 @@ struct FileSyncConfig<VPN: VPNService, FS: FileSyncDaemon>: View {
5050FileSyncSessionModal < VPN , FS > ( existingSession: session)
5151. frame ( width: 700 )
5252} . alert ( " Error " , isPresented: Binding (
53- get: { deleteError != nil } ,
53+ get: { actionError != nil } ,
5454 set: { isPresentedin
5555if !isPresented{
56- deleteError = nil
56+ actionError = nil
5757}
5858}
5959) ) { } message: {
60- Text ( deleteError ? . description?? " An unknown error occurred. " )
60+ Text ( actionError ? . description?? " An unknown error occurred. " )
6161} . alert ( " Error " , isPresented: Binding (
6262 // We only show the alert if the file config window is open
6363 // Users will see the alert symbol on the menu bar to prompt them to
@@ -118,7 +118,7 @@ struct FileSyncConfig<VPN: VPNService, FS: FileSyncDaemon>: View {
118118 addingNewSession= true
119119} label: {
120120Image ( systemName: " plus " )
121- . frame ( width: 24 , height: 24 )
121+ . frame ( width: 24 , height: 24 ) . help ( " Create " )
122122} . disabled ( vpn. menuState. agents. isEmpty)
123123Divider ( )
124124Button {
@@ -133,43 +133,72 @@ struct FileSyncConfig<VPN: VPNService, FS: FileSyncDaemon>: View {
133133await fileSync. stop ( )
134134}
135135} catch{
136- deleteError = error
136+ actionError = error
137137}
138138 selection= nil
139139}
140140} label: {
141- Image ( systemName: " minus " ) . frame ( width: 24 , height: 24 )
141+ Image ( systemName: " minus " ) . frame ( width: 24 , height: 24 ) . help ( " Delete " )
142142} . disabled ( selection== nil )
143- if let selection{
144- if let selectedSession= fileSync. sessionState. first ( where: { $0. id== selection} ) {
145- Divider ( )
146- Button {
147- Task {
148- // TODO: Support pausing & resuming multiple sessions at once
149- loading= true
150- defer { loading= false }
143+ sessionControls
144+ }
145+ . buttonStyle ( . borderless)
146+ }
147+ . background ( . primary. opacity ( 0.04 ) )
148+ . fixedSize ( horizontal: false , vertical: true )
149+ }
150+
151+ var sessionControls : some View {
152+ Group {
153+ if let selection{
154+ if let selectedSession= fileSync. sessionState. first ( where: { $0. id== selection} ) {
155+ Divider ( )
156+ Button {
157+ Task {
158+ // TODO: Support pausing & resuming multiple sessions at once
159+ loading= true
160+ defer { loading= false }
161+ do throws ( DaemonError) {
151162switch selectedSession. status{
152- case . paused:
163+ case . paused, . error( . haltedOnRootEmptied) ,
164+ . error( . haltedOnRootDeletion) ,
165+ . error( . haltedOnRootTypeChange) :
153166try await fileSync. resumeSessions ( ids: [ selectedSession. id] )
154167default :
155168try await fileSync. pauseSessions ( ids: [ selectedSession. id] )
156169}
170+ } catch{
171+ actionError= error
157172}
158- } label: {
159- switch selectedSession. status{
160- case . paused:
161- Image ( systemName: " play " ) . frame ( width: 24 , height: 24 )
162- default :
163- Image ( systemName: " pause " ) . frame ( width: 24 , height: 24 )
173+ }
174+ } label: {
175+ switch selectedSession. status{
176+ case . paused, . error( . haltedOnRootEmptied) ,
177+ . error( . haltedOnRootDeletion) ,
178+ . error( . haltedOnRootTypeChange) :
179+ Image ( systemName: " play " ) . frame ( width: 24 , height: 24 ) . help ( " Pause " )
180+ default :
181+ Image ( systemName: " pause " ) . frame ( width: 24 , height: 24 ) . help ( " Resume " )
182+ }
183+ }
184+ Divider ( )
185+ Button {
186+ Task {
187+ // TODO: Support restarting multiple sessions at once
188+ loading= true
189+ defer { loading= false }
190+ do throws ( DaemonError) {
191+ try await fileSync. resetSessions ( ids: [ selectedSession. id] )
192+ } catch{
193+ actionError= error
164194}
165195}
196+ } label: {
197+ Image ( systemName: " arrow.clockwise " ) . frame ( width: 24 , height: 24 ) . help ( " Reset " )
166198}
167199}
168200}
169- . buttonStyle ( . borderless)
170201}
171- . background ( . primary. opacity ( 0.04 ) )
172- . fixedSize ( horizontal: false , vertical: true )
173202}
174203}
175204