99using GeneralUpdate . Tool . Avalonia . Common ;
1010using GeneralUpdate . Tool . Avalonia . Models ;
1111using Newtonsoft . Json ;
12+ using Nlnet . Avalonia . Controls ;
1213
1314namespace GeneralUpdate . Tool . Avalonia . ViewModels ;
1415
@@ -19,13 +20,13 @@ public class ExtensionViewModel : ObservableObject
1920
2021 private ExtensionConfigModel ? _configModel ;
2122 private AsyncRelayCommand ? _generateCommand ;
22- private AsyncRelayCommand < string > ? _selectFolderCommand ;
23+ private AsyncRelayCommand < string ? > ? _selectFolderCommand ;
2324 private RelayCommand ? _loadedCommand ;
2425 private RelayCommand ? _clearCommand ;
2526 private AsyncRelayCommand ? _selectDependenciesCommand ;
2627 private ExtensionDependencySelectionModel ? _selectedDependency ;
27- private RelayCommand < CustomPropertyModel > ? _removeCustomPropertyCommand ;
28- private RelayCommand ? _addCustomPropertyCommand ;
28+ private AsyncRelayCommand < CustomPropertyModel > ? _removeCustomPropertyCommand ;
29+ private AsyncRelayCommand ? _addCustomPropertyCommand ;
2930 private string ? _newCustomPropertyKey ;
3031 private string ? _newCustomPropertyValue ;
3132
@@ -38,9 +39,9 @@ public RelayCommand LoadedCommand
3839 get { return _loadedCommand ??= new RelayCommand ( LoadedAction ) ; }
3940 }
4041
41- public AsyncRelayCommand < string > SelectFolderCommand
42+ public AsyncRelayCommand < string ? > SelectFolderCommand
4243 {
43- get => _selectFolderCommand ??= new AsyncRelayCommand < string > ( SelectFolderAction ) ;
44+ get => _selectFolderCommand ??= new AsyncRelayCommand < string ? > ( SelectFolderAction ) ;
4445 }
4546
4647 public AsyncRelayCommand GenerateCommand
@@ -76,22 +77,25 @@ public ExtensionDependencySelectionModel? SelectedDependency
7677 set => SetProperty ( ref _selectedDependency , value ) ;
7778 }
7879
79- public RelayCommand < CustomPropertyModel > RemoveCustomPropertyCommand
80+ public AsyncRelayCommand < CustomPropertyModel > RemoveCustomPropertyCommand
8081 {
81- get => _removeCustomPropertyCommand ??= new RelayCommand < CustomPropertyModel > ( RemoveCustomPropertyAction ) ;
82+ get => _removeCustomPropertyCommand ??= new AsyncRelayCommand < CustomPropertyModel > ( RemoveCustomPropertyAction ) ;
8283 }
8384
84- public RelayCommand AddCustomPropertyCommand
85+ public AsyncRelayCommand AddCustomPropertyCommand
8586 {
86- get => _addCustomPropertyCommand ??= new RelayCommand ( AddCustomPropertyAction , CanAddCustomProperty ) ;
87+ get => _addCustomPropertyCommand ??= new AsyncRelayCommand ( AddCustomPropertyAction , CanAddCustomProperty ) ;
8788 }
8889
8990 public string ? NewCustomPropertyKey
9091 {
9192 get => _newCustomPropertyKey ;
9293 set
9394 {
94- SetProperty ( ref _newCustomPropertyKey , value ) ;
95+ if ( SetProperty ( ref _newCustomPropertyKey , value ) )
96+ {
97+ AddCustomPropertyCommand . NotifyCanExecuteChanged ( ) ;
98+ }
9599 }
96100 }
97101
@@ -100,7 +104,10 @@ public string? NewCustomPropertyValue
100104 get => _newCustomPropertyValue ;
101105 set
102106 {
103- SetProperty ( ref _newCustomPropertyValue , value ) ;
107+ if ( SetProperty ( ref _newCustomPropertyValue , value ) )
108+ {
109+ AddCustomPropertyCommand . NotifyCanExecuteChanged ( ) ;
110+ }
104111 }
105112 }
106113
@@ -159,26 +166,42 @@ private void ResetAction()
159166 NewCustomPropertyValue = string . Empty ;
160167 }
161168
162- private async Task SelectFolderAction ( string value )
169+ private async Task SelectFolderAction ( string ? value )
163170 {
164171 try
165172 {
173+ if ( string . IsNullOrWhiteSpace ( value ) )
174+ {
175+ await MessageBox . ShowAsync ( "Invalid folder selection parameter" , "Error" , Buttons . OK ) ;
176+ return ;
177+ }
178+
166179 var folders = await Storage . Instance . SelectFolderDialog ( ) ;
167180 if ( ! folders . Any ( ) ) return ;
168181
169182 var folder = folders . First ( ) ;
183+ if ( folder ? . Path ? . LocalPath == null )
184+ {
185+ await MessageBox . ShowAsync ( "Selected folder path is invalid" , "Error" , Buttons . OK ) ;
186+ return ;
187+ }
188+
170189 switch ( value )
171190 {
172191 case "ExtensionDirectory" :
173- ConfigModel . ExtensionDirectory = folder ! . Path . LocalPath ;
192+ ConfigModel . ExtensionDirectory = folder . Path . LocalPath ;
174193 break ;
175194 case "ExportPath" :
176- ConfigModel . Path = folder ! . Path . LocalPath ;
195+ ConfigModel . Path = folder . Path . LocalPath ;
196+ break ;
197+ default :
198+ await MessageBox . ShowAsync ( $ "Unknown folder selection type: { value } ", "Error" , Buttons . OK ) ;
177199 break ;
178200 }
179201 }
180- catch ( Exception e )
202+ catch ( Exception ex )
181203 {
204+ await MessageBox . ShowAsync ( $ "Failed to select folder: { ex . Message } ", "Error" , Buttons . OK ) ;
182205 }
183206 }
184207
@@ -192,26 +215,31 @@ private async Task GeneratePackageAction()
192215 // Validate input
193216 if ( string . IsNullOrWhiteSpace ( ConfigModel . Name ) )
194217 {
195- //eventAggregator.PublishWarning ("Extension name is required");
218+ await MessageBox . ShowAsync ( "Extension name is required" , "Validation Error" , Buttons . OK ) ;
196219 return ;
197220 }
198221
199222 if ( string . IsNullOrWhiteSpace ( ConfigModel . Version ) )
200223 {
201- //eventAggregator.PublishWarning ("Extension version is required");
224+ await MessageBox . ShowAsync ( "Extension version is required" , "Validation Error" , Buttons . OK ) ;
202225 return ;
203226 }
204227
205- if ( string . IsNullOrWhiteSpace ( ConfigModel . ExtensionDirectory ) ||
206- ! Directory . Exists ( ConfigModel . ExtensionDirectory ) )
228+ if ( string . IsNullOrWhiteSpace ( ConfigModel . ExtensionDirectory ) )
207229 {
208- //eventAggregator.PublishWarning("Extension directory is invalid");
230+ await MessageBox . ShowAsync ( "Extension directory is required" , "Validation Error" , Buttons . OK ) ;
231+ return ;
232+ }
233+
234+ if ( ! Directory . Exists ( ConfigModel . ExtensionDirectory ) )
235+ {
236+ await MessageBox . ShowAsync ( $ "Extension directory does not exist: { ConfigModel . ExtensionDirectory } ", "Validation Error" , Buttons . OK ) ;
209237 return ;
210238 }
211239
212240 if ( string . IsNullOrWhiteSpace ( ConfigModel . Path ) )
213241 {
214- //eventAggregator.PublishWarning ("Export path is required");
242+ await MessageBox . ShowAsync ( "Export path is required" , "Validation Error" , Buttons . OK ) ;
215243 return ;
216244 }
217245
@@ -232,8 +260,6 @@ private async Task GeneratePackageAction()
232260 var zipFileName = $ "{ sanitizedName } _{ sanitizedVersion } .zip";
233261 var zipFilePath = Path . Combine ( exportDirectory , zipFileName ) ;
234262
235- //eventAggregator.PublishSuccess("Starting extension compression...");
236-
237263 // Compress the extension directory into a zip file
238264 await ZipUtility . CompressDirectoryAsync (
239265 ConfigModel . ExtensionDirectory ,
@@ -247,20 +273,42 @@ await ZipUtility.CompressDirectoryAsync(
247273 // Create manifest.json with all ExtensionDTO fields
248274 var platformValue = ConfigModel . Platform ? . Value ?? 0 ;
249275 var targetPlatform = MapPlatformValue ( platformValue ) ;
250- ConfigModel . Platform = new PlatformModel { DisplayName = targetPlatform . ToString ( ) , Value = platformValue } ;
276+ ConfigModel . Platform = new PlatformModel { DisplayName = targetPlatform . ToString ( ) , Value = platformValue } ;
277+
251278 // Get file info for the zip
252279 var fileInfo = new FileInfo ( zipFilePath ) ;
253280 ConfigModel . FileSize = fileInfo . Length ;
254- // Serialize manifest to JSON
255- var manifestJson = JsonConvert . SerializeObject ( ConfigModel ) ;
281+
282+ // Serialize manifest to JSON with explicit settings
283+ var jsonSettings = new JsonSerializerSettings
284+ {
285+ NullValueHandling = NullValueHandling . Ignore
286+ } ;
287+ var manifestJson = JsonConvert . SerializeObject ( ConfigModel , jsonSettings ) ;
256288 if ( ! string . IsNullOrEmpty ( manifestJson ) )
257289 {
258290 // Add manifest.json to the zip file
259291 await ZipUtility . AddFileToZipAsync ( zipFilePath , "manifest.json" , manifestJson ) ;
260292 }
293+
294+ var fileName = Path . GetFileName ( zipFilePath ) ;
295+ var directory = Path . GetDirectoryName ( zipFilePath ) ;
296+ await MessageBox . ShowAsync (
297+ $ "Extension package created successfully:\n \n File: { fileName } \n Location: { directory } ",
298+ "Success" ,
299+ Buttons . OK ) ;
300+ }
301+ catch ( UnauthorizedAccessException ex )
302+ {
303+ await MessageBox . ShowAsync ( $ "Access denied: { ex . Message } \n Please check file permissions.", "Error" , Buttons . OK ) ;
304+ }
305+ catch ( IOException ex )
306+ {
307+ await MessageBox . ShowAsync ( $ "I/O error: { ex . Message } ", "Error" , Buttons . OK ) ;
261308 }
262309 catch ( Exception ex )
263310 {
311+ await MessageBox . ShowAsync ( $ "Failed to generate package: { ex . Message } ", "Error" , Buttons . OK ) ;
264312 }
265313 }
266314
@@ -272,19 +320,26 @@ private bool CanAddCustomProperty()
272320 ! string . IsNullOrWhiteSpace ( NewCustomPropertyValue ) ;
273321 }
274322
275- private void AddCustomPropertyAction ( )
323+ private async Task AddCustomPropertyAction ( )
276324 {
277325 try
278326 {
279- if ( string . IsNullOrWhiteSpace ( NewCustomPropertyKey ) ||
280- string . IsNullOrWhiteSpace ( NewCustomPropertyValue ) )
327+ if ( string . IsNullOrWhiteSpace ( NewCustomPropertyKey ) )
328+ {
329+ await MessageBox . ShowAsync ( "Property key cannot be empty" , "Validation Error" , Buttons . OK ) ;
330+ return ;
331+ }
332+
333+ if ( string . IsNullOrWhiteSpace ( NewCustomPropertyValue ) )
281334 {
335+ await MessageBox . ShowAsync ( "Property value cannot be empty" , "Validation Error" , Buttons . OK ) ;
282336 return ;
283337 }
284338
285339 // Check if key already exists
286340 if ( ConfigModel . CustomProperties . ContainsKey ( NewCustomPropertyKey ) )
287341 {
342+ await MessageBox . ShowAsync ( $ "Property key '{ NewCustomPropertyKey } ' already exists", "Validation Error" , Buttons . OK ) ;
288343 return ;
289344 }
290345
@@ -301,34 +356,35 @@ private void AddCustomPropertyAction()
301356 // Clear input fields
302357 NewCustomPropertyKey = string . Empty ;
303358 NewCustomPropertyValue = string . Empty ;
304-
305359 }
306360 catch ( Exception ex )
307361 {
362+ await MessageBox . ShowAsync ( $ "Failed to add custom property: { ex . Message } ", "Error" , Buttons . OK ) ;
308363 }
309364 }
310365
311- private void RemoveCustomPropertyAction ( CustomPropertyModel ? property )
366+ private async Task RemoveCustomPropertyAction ( CustomPropertyModel ? property )
312367 {
313368 try
314369 {
315370 if ( property == null )
316371 {
372+ await MessageBox . ShowAsync ( "No property selected to remove" , "Validation Error" , Buttons . OK ) ;
317373 return ;
318374 }
319375
320- // Remove from dictionary - use TryGetValue for safety
376+ // Remove from dictionary
321377 if ( ConfigModel . CustomProperties . ContainsKey ( property . Key ) )
322378 {
323379 ConfigModel . CustomProperties . Remove ( property . Key ) ;
324380 }
325381
326382 // Remove from observable collection
327383 CustomPropertiesCollection . Remove ( property ) ;
328-
329384 }
330385 catch ( Exception ex )
331386 {
387+ await MessageBox . ShowAsync ( $ "Failed to remove custom property: { ex . Message } ", "Error" , Buttons . OK ) ;
332388 }
333389 }
334390
0 commit comments