Skip to content

Commit

Permalink
Merge pull request #219 from zadjii-msft/dev/migrie/f/TRA-forms-pr
Browse files Browse the repository at this point in the history
Get the other two types of pages back in. Form pages and Markdown pages. 

![image](https://github.com/user-attachments/assets/27d10b91-6920-421e-912b-a64ca26baf93)

When forms fail to parse, we'll display the exception by replacing the card with one of our one authoring
![image](https://github.com/user-attachments/assets/e3e74d7c-d6da-4d8a-b91b-d66aff8b3d9c)

Markdown pages support multiple bodies, and possibly details:
![image](https://github.com/user-attachments/assets/8abb36e9-d3aa-4d1a-b045-d34d84fe16e6)
![image](https://github.com/user-attachments/assets/047d4bdd-e356-4a70-b62d-4ccd678b7285)



Ref #73
  • Loading branch information
zadjii-msft authored Dec 13, 2024
2 parents 23e94f2 + c5f0881 commit 4ad92b6
Show file tree
Hide file tree
Showing 29 changed files with 1,348 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public MastodonExtensionActionsProvider()
}

[System.Diagnostics.CodeAnalysis.SuppressMessage("StyleCop.CSharp.MaintainabilityRules", "SA1402:File may only contain a single type", Justification = "This is sample code")]
public partial class MastodonPostForm : IForm
public partial class MastodonPostForm : Form
{
private readonly MastodonStatus post;

Expand All @@ -157,7 +157,7 @@ public MastodonPostForm(MastodonStatus post)
this.post = post;
}

public string DataJson()
public override string DataJson()
{
return $$"""
{
Expand All @@ -171,11 +171,9 @@ public string DataJson()
""";
}

public string StateJson() => throw new NotImplementedException();
public override ICommandResult SubmitForm(string payload) => CommandResult.Dismiss();

public ICommandResult SubmitForm(string payload) => CommandResult.Dismiss();

public string TemplateJson()
public override string TemplateJson()
{
var img_block = string.Empty;
if (post.MediaAttachments.Count > 0)
Expand Down Expand Up @@ -203,7 +201,7 @@ public string TemplateJson()
{
"type": "Image",
"url": "${author_avatar_url}",
"size": "Small",
"size": "Medium",
"style": "Person"
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public override string TemplateJson()

public override string DataJson() => throw new NotImplementedException();

public override string StateJson() => throw new NotImplementedException();
public override string StateJson() => "{}";

public override CommandResult SubmitForm(string payload)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public override string TemplateJson()

public override string DataJson() => throw new NotImplementedException();

public override string StateJson() => throw new NotImplementedException();
public override string StateJson() => "{}";

public override CommandResult SubmitForm(string payload)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ private static string GetSettingsDataJson()
""";
}

public override string StateJson() => throw new NotImplementedException();
public override string StateJson() => "{}";

public override CommandResult SubmitForm(string payload)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public override string TemplateJson()
return json;
}

public override string StateJson() => throw new NotImplementedException();
public override string StateJson() => "{}";

public override CommandResult SubmitForm(string payload)
{
Expand Down
33 changes: 23 additions & 10 deletions src/modules/cmdpal/Exts/SamplePagesExtension/SamplesListPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,11 @@ namespace SamplePagesExtension;
public partial class SamplesListPage : ListPage
{
private readonly IListItem[] _commands = [
new ListItem(new SampleMarkdownPage())
{
Title = "Markdown Page Sample Command",
Subtitle = "Display a page of rendered markdown",
},
new ListItem(new SampleListPage())
{
Title = "List Page Sample Command",
Subtitle = "Display a list of items",
},
new ListItem(new SampleFormPage())
{
Title = "Form Page Sample Command",
Subtitle = "Define inputs to retrieve input from the user",
},
new ListItem(new SampleListPageWithDetails())
{
Title = "List Page With Details Sample Command",
Expand All @@ -40,11 +30,34 @@ public partial class SamplesListPage : ListPage
Title = "Dynamic List Page Command",
Subtitle = "Changes the list of items in response to the typed query",
},

new ListItem(new SampleMarkdownPage())
{
Title = "Markdown Page Sample Command",
Subtitle = "Display a page of rendered markdown",
},
new ListItem(new SampleMarkdownManyBodies())
{
Title = "Markdown with multiple blocks",
Subtitle = "A page with multiple blocks of rendered markdown",
},
new ListItem(new SampleMarkdownDetails())
{
Title = "Markdown with details",
Subtitle = "A page with markdown and details",
},

new ListItem(new SampleFormPage())
{
Title = "Form Page Sample Command",
Subtitle = "Define inputs to retrieve input from the user",
},
new ListItem(new SampleSettingsPage())
{
Title = "Sample settings page",
Subtitle = "A demo of the settings helpers",
},

new ListItem(new EvilSamplesPage())
{
Title = "Evil samples",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public override string TemplateJson()

public override string DataJson() => throw new NotImplementedException();

public override string StateJson() => throw new NotImplementedException();
public override string StateJson() => "{}";

public override CommandResult SubmitForm(string payload)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public override string TemplateJson()

public override string DataJson() => throw new NotImplementedException();

public override string StateJson() => throw new NotImplementedException();
public override string StateJson() => "{}";

public override CommandResult SubmitForm(string payload)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ public override void InitializeProperties()
return;
}

Title = model.Title;
Body = model.Body;
Title = model.Title ?? string.Empty;
Body = model.Body ?? string.Empty;
HeroImage = model.HeroImage;

UpdateProperty(nameof(Title));
Expand Down
145 changes: 145 additions & 0 deletions src/modules/cmdpal/Microsoft.CmdPal.UI.ViewModels/FormViewModel.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Text.Json;
using AdaptiveCards.ObjectModel.WinUI3;
using AdaptiveCards.Templating;
using CommunityToolkit.Mvvm.Messaging;
using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.UI.ViewModels.Messages;
using Microsoft.CmdPal.UI.ViewModels.Models;
using Windows.Data.Json;

namespace Microsoft.CmdPal.UI.ViewModels;

public partial class FormViewModel(IForm _form, IPageContext context) : ExtensionObjectViewModel(context)
{
private readonly ExtensionObject<IForm> _formModel = new(_form);

// Remember - "observable" properties from the model (via PropChanged)
// cannot be marked [ObservableProperty]
public string TemplateJson { get; protected set; } = "{}";

public string StateJson { get; protected set; } = "{}";

public string DataJson { get; protected set; } = "{}";

public AdaptiveCardParseResult? Card { get; private set; }

public override void InitializeProperties()
{
var model = _formModel.Unsafe;
if (model == null)
{
return;
}

try
{
TemplateJson = model.TemplateJson();
StateJson = model.StateJson();
DataJson = model.DataJson();

AdaptiveCardTemplate template = new(TemplateJson);
var cardJson = template.Expand(DataJson);
Card = AdaptiveCard.FromJsonString(cardJson);
}
catch (Exception e)
{
// If we fail to parse the card JSON, then display _our own card_
// with the exception
AdaptiveCardTemplate template = new(ErrorCardJson);

// todo: we could probably stick Card.Errrors in there too
var dataJson = $$"""
{
"error_message": {{JsonSerializer.Serialize(e.Message)}},
"error_stack": {{JsonSerializer.Serialize(e.StackTrace)}},
"inner_exception": {{JsonSerializer.Serialize(e.InnerException?.Message)}},
"template_json": {{JsonSerializer.Serialize(TemplateJson)}},
"data_json": {{JsonSerializer.Serialize(DataJson)}}
}
""";
var cardJson = template.Expand(dataJson);
Card = AdaptiveCard.FromJsonString(cardJson);
}

UpdateProperty(nameof(Card));
}

public void HandleSubmit(IAdaptiveActionElement action, JsonObject inputs)
{
if (action is AdaptiveOpenUrlAction openUrlAction)
{
WeakReferenceMessenger.Default.Send<LaunchUriMessage>(new(openUrlAction.Url));
return;
}

if (action is AdaptiveSubmitAction or AdaptiveExecuteAction)
{
// Get the data and inputs
// var data = submitAction.DataJson.Stringify();
var inputString = inputs.Stringify();

// _ = data;
_ = inputString;

try
{
var model = _formModel.Unsafe!;
if (model != null)
{
var result = model.SubmitForm(inputString);

// TODO Handle results
}
}
catch (Exception ex)
{
PageContext.ShowException(ex);
}
}
}

private static readonly string ErrorCardJson = """
{
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.5",
"body": [
{
"type": "TextBlock",
"text": "Error parsing form from extension",
"wrap": true,
"style": "heading",
"size": "ExtraLarge",
"weight": "Bolder",
"color": "Attention"
},
{
"type": "TextBlock",
"wrap": true,
"text": "${error_message}",
"color": "Attention"
},
{
"type": "TextBlock",
"text": "${error_stack}",
"fontType": "Monospace"
},
{
"type": "TextBlock",
"wrap": true,
"text": "Inner exception:"
},
{
"type": "TextBlock",
"wrap": true,
"text": "${inner_exception}",
"color": "Attention"
}
]
}
""";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Collections.ObjectModel;
using CommunityToolkit.Mvvm.ComponentModel;
using Microsoft.CmdPal.Extensions;
using Microsoft.CmdPal.UI.ViewModels.Models;

namespace Microsoft.CmdPal.UI.ViewModels;

public partial class FormsPageViewModel : PageViewModel
{
private readonly ExtensionObject<IFormPage> _model;

[ObservableProperty]
public partial ObservableCollection<FormViewModel> Forms { get; set; } = [];

// Remember - "observable" properties from the model (via PropChanged)
// cannot be marked [ObservableProperty]
public FormsPageViewModel(IFormPage model, TaskScheduler scheduler)
: base(model, scheduler)
{
_model = new(model);
}

//// Run on background thread, from InitializeAsync or Model_ItemsChanged
private void FetchForms()
{
try
{
var newItems = _model.Unsafe!.Forms();

Forms.Clear();

foreach (var item in newItems)
{
FormViewModel viewModel = new(item, this);
viewModel.InitializeProperties();
Forms.Add(viewModel);
}
}
catch (Exception ex)
{
ShowException(ex);
throw;
}
}

public override void InitializeProperties()
{
base.InitializeProperties();

var listPage = _model.Unsafe;
if (listPage == null)
{
return; // throw?
}

FetchForms();
}

protected override void FetchProperty(string propertyName)
{
base.FetchProperty(propertyName);

var model = this._model.Unsafe;
if (model == null)
{
return; // throw?
}

// Do we really not have any here?

// Should `Forms` be observable? That was what ended up footgunning widgets in DevHome, so :shurg:

// switch (propertyName)
// {
// case nameof(ShowDetails):
// this.ShowDetails = model.ShowDetails;
// break;
// case nameof(PlaceholderText):
// this.PlaceholderText = model.PlaceholderText;
// break;
// }
UpdateProperty(propertyName);
}
}
Loading

0 comments on commit 4ad92b6

Please sign in to comment.