Message Templates v2#122
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughAdds template-token support, template metadata (category/tags), a MessageTemplateEngine, SendMessageViewModel integration (save/load/duplicate/update/search, token editors, persistence), dialog UI updates, a StringToBoolConverter, and unit tests for models, engine, and view-model flows. ChangesMessage Templating Feature
Sequence Diagram(s)sequenceDiagram
participant User
participant Dialog
participant ViewModel as SendMessageViewModel
participant Engine as MessageTemplateEngine
participant ServiceBus
User->>Dialog: Load saved template
Dialog->>ViewModel: LoadMessageCommand(activeTemplate)
ViewModel->>Engine: ExtractTokenNames(activeTemplate)
Engine-->>ViewModel: token list
ViewModel->>Dialog: Render TemplateTokenValues
User->>Dialog: Enter token values
Dialog->>ViewModel: SendAsync()
ViewModel->>Engine: FindMissingTokenValues(message, values)
Engine-->>ViewModel: missing tokens (or empty)
alt no missing
ViewModel->>Engine: Apply(message, values)
Engine-->>ViewModel: resolved message
ViewModel->>ServiceBus: SendMessageAsync(resolved message)
ServiceBus-->>ViewModel: send result
ViewModel->>Dialog: Close + status
else missing tokens
ViewModel-->>Dialog: Show ErrorMessage (missing tokens)
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
BusLane/Views/Dialogs/LoadMessageDialog.axaml (1)
84-86:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winEmpty-state vs. filtered list mismatch (same as SendMessageDialog).
The
ListBoxnow binds toFilteredSavedMessages(Line 100), but this empty-state border is still driven bySavedMessages.Count. A search that filters everything out shows neither rows nor the empty state. Bind to the filtered collection so the empty/no-results feedback stays correct.🔧 Proposed fix
<Border DockPanel.Dock="Top" Classes="empty-state" - IsVisible="{Binding !SendMessageViewModel.SavedMessages.Count}" + IsVisible="{Binding !SendMessageViewModel.FilteredSavedMessages.Count}" Margin="16">🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@BusLane/Views/Dialogs/LoadMessageDialog.axaml` around lines 84 - 86, The empty-state Border's visibility is still driven by SendMessageViewModel.SavedMessages.Count while the ListBox uses FilteredSavedMessages; update the Border's IsVisible binding to use the filtered collection (e.g. bind to SendMessageViewModel.FilteredSavedMessages.Count or a boolean exposed for "HasFilteredItems") so the empty/no-results UI matches the ListBox contents (look for the Border with Classes="empty-state" and the ListBox bound to FilteredSavedMessages).BusLane/Views/Dialogs/SendMessageDialog.axaml (1)
123-127:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winEmpty-state no longer matches the filtered list.
The list switched to
FilteredSavedMessages(Line 85), but this "No saved templates yet" placeholder is still gated onSavedMessages.Count. When a user types a query that matches nothing, theItemsControlrenders empty and the placeholder stays hidden, leaving a blank flyout with no feedback. Bind the placeholder to the filtered collection instead.🔧 Proposed fix
<TextBlock Text="No saved templates yet" Classes="caption" HorizontalAlignment="Center" Margin="0,12" - IsVisible="{Binding !SendMessageViewModel.SavedMessages.Count}"/> + IsVisible="{Binding !SendMessageViewModel.FilteredSavedMessages.Count}"/>🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@BusLane/Views/Dialogs/SendMessageDialog.axaml` around lines 123 - 127, The empty-state TextBlock is still bound to SavedMessages.Count so it doesn't show when the filtered list is empty; change its IsVisible binding to use the filtered collection (e.g. bind IsVisible to "!SendMessageViewModel.FilteredSavedMessages.Count") so the "No saved templates yet" placeholder appears when FilteredSavedMessages is empty; update the TextBlock in SendMessageDialog.axaml that currently references SavedMessages.Count to reference FilteredSavedMessages.Count instead.BusLane/ViewModels/SendMessageViewModel.cs (2)
243-249:⚠️ Potential issue | 🟠 Major | ⚡ Quick winClear the active template when deleting it.
If the removed item is
ActiveTemplate,HasActiveTemplatestays true andUpdateActiveTemplatecan still mutate that detached object, then report success even though it is no longer inSavedMessages.Suggested fix
private void DeleteSavedMessage(SavedMessage message) { + var isActiveTemplate = ActiveTemplate?.Id == message.Id; + SavedMessages.Remove(message); + if (isActiveTemplate) + { + TemplateTokenValues.Clear(); + ActiveTemplate = null; + } + OnPropertyChanged(nameof(FilteredSavedMessages)); PersistSavedMessages(); }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@BusLane/ViewModels/SendMessageViewModel.cs` around lines 243 - 249, The DeleteSavedMessage method currently removes the message but doesn't clear ActiveTemplate, leaving HasActiveTemplate true and allowing UpdateActiveTemplate to mutate a removed object; modify DeleteSavedMessage (in SendMessageViewModel) so that if the removed SavedMessage equals ActiveTemplate you set ActiveTemplate to null (or call UpdateActiveTemplate(null)/clear the active template via its setter), raise PropertyChanged for HasActiveTemplate and FilteredSavedMessages as needed, then call PersistSavedMessages; this ensures the active template is cleared before persisting and prevents operations on a detached object.
549-555:⚠️ Potential issue | 🟠 Major | ⚡ Quick winSurface persistence failures instead of swallowing them.
SaveMessage,DeleteSavedMessage,DuplicateSavedMessage, andUpdateActiveTemplateall assume this write succeeds and immediately update the UI as if disk state is durable. IfCreateSecureFilefails, the in-memory collection diverges from disk and the user only discovers the loss after restart.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@BusLane/ViewModels/SendMessageViewModel.cs` around lines 549 - 555, PersistSavedMessages currently swallows exceptions from AppPaths.CreateSecureFile causing in-memory SavedMessages (and callers SaveMessage, DeleteSavedMessage, DuplicateSavedMessage, UpdateActiveTemplate) to diverge from disk; change PersistSavedMessages to propagate failures (either by throwing the caught exception or returning a failure result) and ensure callers handle that result by rolling back the in-memory change and surfacing an error to the UI/log. Specifically, update PersistSavedMessages to not catch-and-ignore exceptions from Serialize or AppPaths.CreateSecureFile (or to return bool/Result), and then modify SaveMessage, DeleteSavedMessage, DuplicateSavedMessage, and UpdateActiveTemplate to detect persistence failure and revert SavedMessages and any UI state changes (and notify the user via the existing error reporting mechanism) so disk/write errors are visible and memory/disk stay consistent.
🧹 Nitpick comments (1)
BusLane/Models/SavedMessage.cs (1)
25-47: ⚡ Quick winAdd XML doc comments to new public API surface.
SavedMessage.Duplicate(...)(Line 25) andTemplateTokenValue(Line 56) are public and currently undocumented; please add XML docs for these public members.As per coding guidelines, "Use XML doc comments for public classes, methods, and key regions".
Also applies to: 56-60
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@BusLane/Models/SavedMessage.cs` around lines 25 - 47, Add XML documentation for the new public API members: add a summary, param and returns tags to SavedMessage.Duplicate(string? name = null) explaining that it creates and returns a copy of the SavedMessage with an optional new name (describe that Name defaults to "{Name} Copy" when name is null), and add XML docs for TemplateTokenValue (describe what the type/property represents and any usage/constraints). Ensure the XML comments are placed immediately above the method declaration for Duplicate and above the TemplateTokenValue declaration and include <summary>, <param> for the name parameter, and <returns> for Duplicate; for TemplateTokenValue include at least a <summary> and any relevant remarks.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@BusLane/Services/Templates/MessageTemplateEngine.cs`:
- Around line 28-32: FindMissingTokenValues flags tokens as missing using
values.TryGetValue which respects the dictionary's comparer; change the check to
perform a case-insensitive lookup so casing matches how Apply replaces tokens.
In FindMissingTokenValues, instead of TryGetValue(token, out var value), locate
the matching entry with a case-insensitive comparison (e.g., find
values.Keys/entries where String.Equals(key, token,
StringComparison.OrdinalIgnoreCase)), then inspect that entry's value for
null/whitespace; keep using ExtractTokenNames and ensure behavior matches
Apply's case-insensitive replacement logic.
In `@BusLane/ViewModels/SendMessageViewModel.cs`:
- Around line 230-231: The TemplateTokenValues are only refreshed in
LoadMessage, so placeholders added/edited in the compose form are not shown and
SendAsync can fail; update the code to recompute tokens after the form is edited
by calling RefreshTemplateTokenValues(...) with the up-to-date message returned
by BuildSavedMessageFromForm() whenever templated fields or custom properties
change (e.g. replace the existing ActiveTemplate assignment lines using
ActiveTemplate = message; RefreshTemplateTokenValues(message); and add
equivalent calls in the form-change handlers), and when merging preserve
previously entered token values where keys match so user-entered values are
retained; apply the same change pattern to the other affected block around the
614-621 section (use the same function names: LoadMessage,
BuildSavedMessageFromForm, RefreshTemplateTokenValues, TemplateTokenValues,
SendAsync).
In `@BusLane/Views/Dialogs/LoadMessageDialog.axaml`:
- Line 124: The StackPanel's IsVisible is binding a string property Category
directly (IsVisible="{Binding Category}"), which requires a explicit string→bool
conversion; add a converter (e.g., implement IValueConverter named
StringToBoolConverter or IsNullOrEmptyToBoolConverter that returns false for
null/empty and true otherwise), register it as a XAML resource, and update the
StackPanel IsVisible binding to use that converter (IsVisible="{Binding
Category, Converter={StaticResource StringToBoolConverter}}") so empty or
non-boolean strings correctly map to false/true.
---
Outside diff comments:
In `@BusLane/ViewModels/SendMessageViewModel.cs`:
- Around line 243-249: The DeleteSavedMessage method currently removes the
message but doesn't clear ActiveTemplate, leaving HasActiveTemplate true and
allowing UpdateActiveTemplate to mutate a removed object; modify
DeleteSavedMessage (in SendMessageViewModel) so that if the removed SavedMessage
equals ActiveTemplate you set ActiveTemplate to null (or call
UpdateActiveTemplate(null)/clear the active template via its setter), raise
PropertyChanged for HasActiveTemplate and FilteredSavedMessages as needed, then
call PersistSavedMessages; this ensures the active template is cleared before
persisting and prevents operations on a detached object.
- Around line 549-555: PersistSavedMessages currently swallows exceptions from
AppPaths.CreateSecureFile causing in-memory SavedMessages (and callers
SaveMessage, DeleteSavedMessage, DuplicateSavedMessage, UpdateActiveTemplate) to
diverge from disk; change PersistSavedMessages to propagate failures (either by
throwing the caught exception or returning a failure result) and ensure callers
handle that result by rolling back the in-memory change and surfacing an error
to the UI/log. Specifically, update PersistSavedMessages to not catch-and-ignore
exceptions from Serialize or AppPaths.CreateSecureFile (or to return
bool/Result), and then modify SaveMessage, DeleteSavedMessage,
DuplicateSavedMessage, and UpdateActiveTemplate to detect persistence failure
and revert SavedMessages and any UI state changes (and notify the user via the
existing error reporting mechanism) so disk/write errors are visible and
memory/disk stay consistent.
In `@BusLane/Views/Dialogs/LoadMessageDialog.axaml`:
- Around line 84-86: The empty-state Border's visibility is still driven by
SendMessageViewModel.SavedMessages.Count while the ListBox uses
FilteredSavedMessages; update the Border's IsVisible binding to use the filtered
collection (e.g. bind to SendMessageViewModel.FilteredSavedMessages.Count or a
boolean exposed for "HasFilteredItems") so the empty/no-results UI matches the
ListBox contents (look for the Border with Classes="empty-state" and the ListBox
bound to FilteredSavedMessages).
In `@BusLane/Views/Dialogs/SendMessageDialog.axaml`:
- Around line 123-127: The empty-state TextBlock is still bound to
SavedMessages.Count so it doesn't show when the filtered list is empty; change
its IsVisible binding to use the filtered collection (e.g. bind IsVisible to
"!SendMessageViewModel.FilteredSavedMessages.Count") so the "No saved templates
yet" placeholder appears when FilteredSavedMessages is empty; update the
TextBlock in SendMessageDialog.axaml that currently references
SavedMessages.Count to reference FilteredSavedMessages.Count instead.
---
Nitpick comments:
In `@BusLane/Models/SavedMessage.cs`:
- Around line 25-47: Add XML documentation for the new public API members: add a
summary, param and returns tags to SavedMessage.Duplicate(string? name = null)
explaining that it creates and returns a copy of the SavedMessage with an
optional new name (describe that Name defaults to "{Name} Copy" when name is
null), and add XML docs for TemplateTokenValue (describe what the type/property
represents and any usage/constraints). Ensure the XML comments are placed
immediately above the method declaration for Duplicate and above the
TemplateTokenValue declaration and include <summary>, <param> for the name
parameter, and <returns> for Duplicate; for TemplateTokenValue include at least
a <summary> and any relevant remarks.
🪄 Autofix (Beta)
✅ Autofix completed
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 32a63c5f-99a7-4778-8248-eb5b0c725a58
📒 Files selected for processing (9)
BusLane.Tests/Models/ModelTests.csBusLane.Tests/Services/Templates/MessageTemplateEngineTests.csBusLane.Tests/ViewModels/SendMessageViewModelTests.csBusLane/Models/SavedMessage.csBusLane/Services/Templates/MessageTemplateEngine.csBusLane/ViewModels/SendMessageViewModel.csBusLane/Views/Dialogs/LoadMessageDialog.axamlBusLane/Views/Dialogs/SaveMessageDialog.axamlBusLane/Views/Dialogs/SendMessageDialog.axaml
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Fixes Applied SuccessfullyFixed 4 file(s) based on 3 unresolved review comments. Files modified:
Commit: The changes have been pushed to the Time taken: |
Fixed 4 file(s) based on 3 unresolved review comments. Co-authored-by: CodeRabbit <noreply@coderabbit.ai>
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
BusLane/Models/SavedMessage.cs (1)
5-5: 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick winMake
Idproperty init-only to prevent mutation.The
Idproperty is auto-generated on initialization but allows modification after creation, which could lead to data integrity issues. Mark it asinit-only.🔒 Proposed fix
- public string Id { get; set; } = Guid.NewGuid().ToString(); + public string Id { get; init; } = Guid.NewGuid().ToString();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@BusLane/Models/SavedMessage.cs` at line 5, The Id property on SavedMessage is currently mutable (public string Id { get; set; }) and should be init-only to prevent post-construction mutation; change the property declaration to use an init accessor (public string Id { get; init; } = Guid.NewGuid().ToString();) and update any code that assigns to SavedMessage.Id after construction to instead set it at object creation or remove such assignments.
🧹 Nitpick comments (4)
BusLane/Models/SavedMessage.cs (4)
25-47: ⚡ Quick winAdd XML doc comments for public method.
The
Duplicatemethod is a public API and should have XML documentation describing its behavior and parameters. As per coding guidelines, "Use XML doc comments for public classes, methods, and key regions."📝 Proposed documentation
+ /// <summary> + /// Creates a duplicate of the current SavedMessage with a new Id and CreatedAt timestamp. + /// </summary> + /// <param name="name">Optional custom name for the duplicate. Defaults to "{Name} Copy" if not specified.</param> + /// <returns>A new SavedMessage instance with copied properties and collections.</returns> public SavedMessage Duplicate(string? name = null)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@BusLane/Models/SavedMessage.cs` around lines 25 - 47, Add XML documentation for the public method SavedMessage.Duplicate: include a summary describing that it creates and returns a shallow copy of the current SavedMessage with an optional new name, document the parameter "name" (nullable) explaining that if null the new name is "{Name} Copy", and document the return value as a new SavedMessage instance with copied properties (mention Tags, TokenValues, CustomProperties are copied into new collections). Place the XML comments immediately above the Duplicate method declaration.
50-50: ⚡ Quick winAdd XML doc comments for public class.
Public classes should have XML documentation comments. As per coding guidelines, "Use XML doc comments for public classes, methods, and key regions."
📝 Proposed documentation
+/// <summary> +/// Represents a custom property key-value pair with property change notification support for data binding. +/// </summary> public class CustomProperty : System.ComponentModel.INotifyPropertyChanged🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@BusLane/Models/SavedMessage.cs` at line 50, Add XML documentation for the public class CustomProperty (which implements System.ComponentModel.INotifyPropertyChanged): add a <summary> describing the class responsibility/purpose and note that it implements INotifyPropertyChanged, and optionally include a <remarks> describing usage or thread-safety and a <seealso> or <seealso cref="System.ComponentModel.INotifyPropertyChanged"/> reference; ensure the XML doc sits immediately above the public class declaration for CustomProperty.
84-88: ⚡ Quick winConsider using a record type for immutable data model.
TemplateTokenValueappears to be a simple data container. According to the coding guidelines, "Use records with primary constructors for immutable data models." Converting to a record provides structural equality and clearer intent.♻️ Proposed refactor to record type
-public class TemplateTokenValue -{ - public string Name { get; set; } = ""; - public string Value { get; set; } = ""; -} +/// <summary> +/// Represents a template token name-value pair for token replacement in message templates. +/// </summary> +public record TemplateTokenValue +{ + public required string Name { get; init; } + public required string Value { get; init; } +}Or use primary constructor syntax:
-public class TemplateTokenValue -{ - public string Name { get; set; } = ""; - public string Value { get; set; } = ""; -} +/// <summary> +/// Represents a template token name-value pair for token replacement in message templates. +/// </summary> +public record TemplateTokenValue(string Name, string Value);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@BusLane/Models/SavedMessage.cs` around lines 84 - 88, The TemplateTokenValue class is a simple data container and should be converted to an immutable record: replace the class TemplateTokenValue (with mutable properties Name and Value) with a record using a primary constructor that takes string Name and string Value so you get immutable properties, structural equality, and clearer intent; update any call sites that relied on property setters to construct new instances instead of mutating existing ones.
3-3: ⚡ Quick winAdd XML doc comments for public class.
Public classes should have XML documentation comments describing their purpose. As per coding guidelines, "Use XML doc comments for public classes, methods, and key regions."
📝 Proposed documentation
+/// <summary> +/// Represents a saved Service Bus message template with metadata, properties, and token support. +/// </summary> public class SavedMessage🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@BusLane/Models/SavedMessage.cs` at line 3, Add an XML documentation comment above the public class SavedMessage that explains the purpose of the class (e.g., what it represents and how it is used); include at minimum a <summary> tag describing the class, and optionally <remarks> or <param>/<returns> for any public members within SavedMessage to follow the project's XML doc guideline. Ensure the comment sits immediately before the declaration "public class SavedMessage" and follows the project's style (triple-slash /// XML comments).
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@BusLane/Models/SavedMessage.cs`:
- Line 5: The Id property on SavedMessage is currently mutable (public string Id
{ get; set; }) and should be init-only to prevent post-construction mutation;
change the property declaration to use an init accessor (public string Id { get;
init; } = Guid.NewGuid().ToString();) and update any code that assigns to
SavedMessage.Id after construction to instead set it at object creation or
remove such assignments.
---
Nitpick comments:
In `@BusLane/Models/SavedMessage.cs`:
- Around line 25-47: Add XML documentation for the public method
SavedMessage.Duplicate: include a summary describing that it creates and returns
a shallow copy of the current SavedMessage with an optional new name, document
the parameter "name" (nullable) explaining that if null the new name is "{Name}
Copy", and document the return value as a new SavedMessage instance with copied
properties (mention Tags, TokenValues, CustomProperties are copied into new
collections). Place the XML comments immediately above the Duplicate method
declaration.
- Line 50: Add XML documentation for the public class CustomProperty (which
implements System.ComponentModel.INotifyPropertyChanged): add a <summary>
describing the class responsibility/purpose and note that it implements
INotifyPropertyChanged, and optionally include a <remarks> describing usage or
thread-safety and a <seealso> or <seealso
cref="System.ComponentModel.INotifyPropertyChanged"/> reference; ensure the XML
doc sits immediately above the public class declaration for CustomProperty.
- Around line 84-88: The TemplateTokenValue class is a simple data container and
should be converted to an immutable record: replace the class TemplateTokenValue
(with mutable properties Name and Value) with a record using a primary
constructor that takes string Name and string Value so you get immutable
properties, structural equality, and clearer intent; update any call sites that
relied on property setters to construct new instances instead of mutating
existing ones.
- Line 3: Add an XML documentation comment above the public class SavedMessage
that explains the purpose of the class (e.g., what it represents and how it is
used); include at minimum a <summary> tag describing the class, and optionally
<remarks> or <param>/<returns> for any public members within SavedMessage to
follow the project's XML doc guideline. Ensure the comment sits immediately
before the declaration "public class SavedMessage" and follows the project's
style (triple-slash /// XML comments).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 46037ea9-00d8-46a6-999e-65455cbc03da
📒 Files selected for processing (4)
BusLane/Models/SavedMessage.csBusLane/Services/Templates/MessageTemplateEngine.csBusLane/ViewModels/SendMessageViewModel.csBusLane/Views/Dialogs/LoadMessageDialog.axaml
🚧 Files skipped from review as they are similar to previous changes (3)
- BusLane/Views/Dialogs/LoadMessageDialog.axaml
- BusLane/Services/Templates/MessageTemplateEngine.cs
- BusLane/ViewModels/SendMessageViewModel.cs
Summary
{{Token}}replacement and missing-value validation before send.Validation
dotnet builddotnet testSummary by CodeRabbit
New Features
Tests