88using Lang . Avalonia ;
99using Prism . Regions ;
1010using ReactiveUI ;
11+ using System . Globalization ;
1112using System . Collections . ObjectModel ;
1213using System . Reactive ;
1314using System . Reactive . Disposables ;
@@ -19,15 +20,20 @@ public sealed class ToolViewModel : ReactiveObject, INavigationAware, IDisposabl
1920{
2021 private readonly ToolRegistry _registry ;
2122 private readonly IFileChooserService _fileChooserService ;
23+ private readonly IUserProfileService _userProfileService ;
2224 private readonly CompositeDisposable _subscriptions = new ( ) ;
2325 private CancellationTokenSource ? _runCts ;
2426 private ToolSpec ? _currentTool ;
2527 private string _errorMessage = string . Empty ;
2628
27- public ToolViewModel ( ToolRegistry registry , IFileChooserService fileChooserService )
29+ public ToolViewModel (
30+ ToolRegistry registry ,
31+ IFileChooserService fileChooserService ,
32+ IUserProfileService userProfileService )
2833 {
2934 _registry = registry ;
3035 _fileChooserService = fileChooserService ;
36+ _userProfileService = userProfileService ;
3137 RunCommand = ReactiveCommand . CreateFromTask ( RunAsync ) ;
3238 BrowsePathCommand = ReactiveCommand . CreateFromTask < ToolField > ( BrowsePathAsync ) ;
3339 CopyOutputCommand = ReactiveCommand . CreateFromTask < ToolOutput > ( CopyOutputAsync ) ;
@@ -103,29 +109,22 @@ private void LoadTool(string id)
103109 return ;
104110 }
105111
112+ var savedFieldValues = _userProfileService . GetToolFieldValues ( _currentTool . Id ) ;
106113 foreach ( var field in _currentTool . Fields )
107114 {
115+ RestoreFieldValue ( field , savedFieldValues ) ;
108116 Fields . Add ( field ) ;
109117
118+ AddFieldCacheSubscriptions ( field ) ;
119+
110120 if ( ! _currentTool . AutoRun )
111121 {
112122 continue ;
113123 }
114124
115- _subscriptions . Add ( field . WhenAnyValue ( x => x . Text )
116- . Skip ( 1 )
117- . Throttle ( TimeSpan . FromMilliseconds ( 300 ) )
118- . Subscribe ( _ => Dispatcher . UIThread . Post ( async ( ) => await RunAsync ( ) ) ) ) ;
119- _subscriptions . Add ( field . WhenAnyValue ( x => x . Number )
120- . Skip ( 1 )
125+ _subscriptions . Add ( CreateFieldChangeSignal ( field )
121126 . Throttle ( TimeSpan . FromMilliseconds ( 300 ) )
122127 . Subscribe ( _ => Dispatcher . UIThread . Post ( async ( ) => await RunAsync ( ) ) ) ) ;
123- _subscriptions . Add ( field . WhenAnyValue ( x => x . Boolean )
124- . Skip ( 1 )
125- . Subscribe ( _ => Dispatcher . UIThread . Post ( async ( ) => await RunAsync ( ) ) ) ) ;
126- _subscriptions . Add ( field . WhenAnyValue ( x => x . SelectedOption )
127- . Skip ( 1 )
128- . Subscribe ( _ => Dispatcher . UIThread . Post ( async ( ) => await RunAsync ( ) ) ) ) ;
129128 }
130129
131130 foreach ( var output in _currentTool . Outputs )
@@ -165,6 +164,7 @@ private async Task RunAsync()
165164 ErrorMessage = string . Empty ;
166165 this . RaisePropertyChanged ( nameof ( HasError ) ) ;
167166 await _currentTool . RunAsync ( context , token ) ;
167+ SaveCurrentFieldValues ( ) ;
168168 }
169169 catch ( OperationCanceledException )
170170 {
@@ -221,6 +221,82 @@ public void OnKeyDown(string key, string physicalKey, string modifiers)
221221 context . SetText ( "result" , $ "Key: { key } { Environment . NewLine } Physical key: { physicalKey } { Environment . NewLine } Modifiers: { modifiers } ") ;
222222 }
223223
224+ private void AddFieldCacheSubscriptions ( ToolField field )
225+ {
226+ _subscriptions . Add ( CreateFieldChangeSignal ( field )
227+ . Throttle ( TimeSpan . FromMilliseconds ( 400 ) )
228+ . Subscribe ( _ => SaveCurrentFieldValues ( ) ) ) ;
229+ }
230+
231+ private static IObservable < Unit > CreateFieldChangeSignal ( ToolField field )
232+ {
233+ return Observable . Merge (
234+ field . WhenAnyValue ( x => x . Text ) . Skip ( 1 ) . Select ( _ => Unit . Default ) ,
235+ field . WhenAnyValue ( x => x . Number ) . Skip ( 1 ) . Select ( _ => Unit . Default ) ,
236+ field . WhenAnyValue ( x => x . Boolean ) . Skip ( 1 ) . Select ( _ => Unit . Default ) ,
237+ field . WhenAnyValue ( x => x . SelectedOption ) . Skip ( 1 ) . Select ( _ => Unit . Default ) ) ;
238+ }
239+
240+ private static void RestoreFieldValue ( ToolField field , IReadOnlyDictionary < string , string > savedFieldValues )
241+ {
242+ if ( ! savedFieldValues . TryGetValue ( field . Id , out var value ) )
243+ {
244+ return ;
245+ }
246+
247+ switch ( field . Kind )
248+ {
249+ case ToolFieldKind . Text :
250+ case ToolFieldKind . MultiLine :
251+ case ToolFieldKind . File :
252+ case ToolFieldKind . SaveFile :
253+ field . Text = value ;
254+ break ;
255+ case ToolFieldKind . Number :
256+ if ( decimal . TryParse ( value , NumberStyles . Number , CultureInfo . InvariantCulture , out var number ) )
257+ {
258+ field . Number = number ;
259+ }
260+ break ;
261+ case ToolFieldKind . Boolean :
262+ if ( bool . TryParse ( value , out var boolean ) )
263+ {
264+ field . Boolean = boolean ;
265+ }
266+ break ;
267+ case ToolFieldKind . Select :
268+ field . SelectedOption = field . Options . FirstOrDefault ( option =>
269+ string . Equals ( option . Value , value , StringComparison . OrdinalIgnoreCase ) ) ;
270+ break ;
271+ }
272+ }
273+
274+ private void SaveCurrentFieldValues ( )
275+ {
276+ if ( _currentTool == null || Fields . Count == 0 )
277+ {
278+ return ;
279+ }
280+
281+ var values = Fields . ToDictionary (
282+ field => field . Id ,
283+ GetFieldValue ,
284+ StringComparer . OrdinalIgnoreCase ) ;
285+ _userProfileService . SaveToolFieldValues ( _currentTool . Id , values ) ;
286+ }
287+
288+ private static string GetFieldValue ( ToolField field )
289+ {
290+ return field . Kind switch
291+ {
292+ ToolFieldKind . Text or ToolFieldKind . MultiLine or ToolFieldKind . File or ToolFieldKind . SaveFile => field . Text ,
293+ ToolFieldKind . Number => field . Number . ToString ( CultureInfo . InvariantCulture ) ,
294+ ToolFieldKind . Boolean => field . Boolean . ToString ( ) ,
295+ ToolFieldKind . Select => field . SelectedOption ? . Value ?? string . Empty ,
296+ _ => string . Empty
297+ } ;
298+ }
299+
224300 private static string Translate ( string key )
225301 {
226302 return I18nManager . Instance . GetResource ( key ) ?? key ;
0 commit comments