ContentFormViewModel.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.Diagnostics.CodeAnalysis; 6 using System.Text.Json; 7 using AdaptiveCards.ObjectModel.WinUI3; 8 using AdaptiveCards.Templating; 9 using CommunityToolkit.Mvvm.Messaging; 10 using ManagedCommon; 11 using Microsoft.CmdPal.Core.ViewModels; 12 using Microsoft.CmdPal.Core.ViewModels.Messages; 13 using Microsoft.CmdPal.Core.ViewModels.Models; 14 using Microsoft.CommandPalette.Extensions; 15 using Windows.Data.Json; 16 17 namespace Microsoft.CmdPal.UI.ViewModels; 18 19 public partial class ContentFormViewModel(IFormContent _form, WeakReference<IPageContext> context) : 20 ContentViewModel(context) 21 { 22 private readonly ExtensionObject<IFormContent> _formModel = new(_form); 23 24 // Remember - "observable" properties from the model (via PropChanged) 25 // cannot be marked [ObservableProperty] 26 public string TemplateJson { get; protected set; } = "{}"; 27 28 public string StateJson { get; protected set; } = "{}"; 29 30 public string DataJson { get; protected set; } = "{}"; 31 32 public AdaptiveCardParseResult? Card { get; private set; } 33 34 private static string Serialize(string? s) => 35 JsonSerializer.Serialize(s, JsonSerializationContext.Default.String); 36 37 private static bool TryBuildCard( 38 string templateJson, 39 string dataJson, 40 out AdaptiveCardParseResult? card, 41 out Exception? error) 42 { 43 card = null; 44 error = null; 45 46 try 47 { 48 var template = new AdaptiveCardTemplate(templateJson); 49 var cardJson = template.Expand(dataJson); 50 card = AdaptiveCard.FromJsonString(cardJson); 51 return true; 52 } 53 catch (Exception ex) 54 { 55 Logger.LogError("Error building card from template", ex); 56 error = ex; 57 return false; 58 } 59 } 60 61 public override void InitializeProperties() 62 { 63 var model = _formModel.Unsafe; 64 if (model is null) 65 { 66 return; 67 } 68 69 TemplateJson = model.TemplateJson; 70 StateJson = model.StateJson; 71 DataJson = model.DataJson; 72 73 if (TryBuildCard(TemplateJson, DataJson, out var builtCard, out var renderingError)) 74 { 75 Card = builtCard; 76 UpdateProperty(nameof(Card)); 77 return; 78 } 79 80 var errorPayload = $$""" 81 { 82 "error_message": {{Serialize(renderingError!.Message)}}, 83 "error_stack": {{Serialize(renderingError.StackTrace)}}, 84 "inner_exception": {{Serialize(renderingError.InnerException?.Message)}}, 85 "template_json": {{Serialize(TemplateJson)}}, 86 "data_json": {{Serialize(DataJson)}} 87 } 88 """; 89 90 if (TryBuildCard(ErrorCardJson, errorPayload, out var errorCard, out var _)) 91 { 92 Card = errorCard; 93 UpdateProperty(nameof(Card)); 94 return; 95 } 96 97 UpdateProperty(nameof(Card)); 98 } 99 100 [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(AdaptiveOpenUrlAction))] 101 [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(AdaptiveSubmitAction))] 102 [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(AdaptiveExecuteAction))] 103 public void HandleSubmit(IAdaptiveActionElement action, JsonObject inputs) 104 { 105 if (action is AdaptiveOpenUrlAction openUrlAction) 106 { 107 WeakReferenceMessenger.Default.Send<LaunchUriMessage>(new(openUrlAction.Url)); 108 return; 109 } 110 111 if (action is AdaptiveSubmitAction or AdaptiveExecuteAction) 112 { 113 // Get the data and inputs 114 var dataString = (action as AdaptiveSubmitAction)?.DataJson.Stringify() ?? string.Empty; 115 var inputString = inputs.Stringify(); 116 117 _ = Task.Run(() => 118 { 119 try 120 { 121 var model = _formModel.Unsafe!; 122 if (model != null) 123 { 124 var result = model.SubmitForm(inputString, dataString); 125 WeakReferenceMessenger.Default.Send<HandleCommandResultMessage>(new(new(result))); 126 } 127 } 128 catch (Exception ex) 129 { 130 ShowException(ex); 131 } 132 }); 133 } 134 } 135 136 private static readonly string ErrorCardJson = """ 137 { 138 "$schema": "http://adaptivecards.io/schemas/adaptive-card.json", 139 "type": "AdaptiveCard", 140 "version": "1.5", 141 "body": [ 142 { 143 "type": "TextBlock", 144 "text": "Error parsing form from extension", 145 "wrap": true, 146 "style": "heading", 147 "size": "ExtraLarge", 148 "weight": "Bolder", 149 "color": "Attention" 150 }, 151 { 152 "type": "TextBlock", 153 "wrap": true, 154 "text": "${error_message}", 155 "color": "Attention" 156 }, 157 { 158 "type": "TextBlock", 159 "text": "${error_stack}", 160 "fontType": "Monospace" 161 }, 162 { 163 "type": "TextBlock", 164 "wrap": true, 165 "text": "Inner exception:" 166 }, 167 { 168 "type": "TextBlock", 169 "wrap": true, 170 "text": "${inner_exception}", 171 "color": "Attention" 172 } 173 ] 174 } 175 """; 176 }