Thanks for the inspiration
@WarrenBelz! I ended up scraping the idea of using the "AT" symbol, and decided to go with comment tags outside of the comment dialog and I am pretty happy with it. I have not been able to find any versions of comments for canvas with replies, so I was happy to get this working.
Note that for this case I have a gallery of Projects (not shown here) that the comments are part of. So each project has its own comments section. You could implement this with no projects table.
I ended up taking a different route, but I am feeling pretty good about where things have landed.
This is what my comments section looks like:
Data Structure (Tables in Dataverse in my case, but I can imagine a Sharepoint version):
Comments Table - Unique ID, Comment Text, Project ID/lookup (optional if the comments are associated with a particular record). For reference, I am using an auto increment ID in the Dataverse Name field (so when you see Project.Name in my code below, it is referring to this unique autonumber from the referenced table).
Replies Table - Unique ID, Comment ID (or lookup to comments table), Reply Text
Comment Tags Table - Unique ID, Lookup to Comments Table, User field (could be Microsoft Entra ID Table Lookup)
Reply Tags Table - Unique ID, Lookup to Replies Table, User field (could be Microsoft Entra ID Table Lookup)
Screen Structure
There is a main gallery for Comments, with a sub gallery added inside of the gallery rows (added to the first/template row) for replies. The Replies Table is mapped to the Comments Table by filtering on the Project ID.
Items in the Replies Table:
Filter('Comment Replies',Comment.Name=TextCanvas13.Text)
The TextCanvas13.Text is a text label in the comments gallery.
There is a comment input field with a send button, a tags dropdown and a tags label:
The "Tag People" dropdown is populated in my case with the Microsoft Entra ID Dataverse Table (this is available by default in Dataverse). I can imagine this being populated by Office365Users as well. When the user selects a person to tag from the dropdown, I am grabbing the user from the dropdown and adding them one at a time (not multi select) to a collection. The following is added to the onchange of the dropdown. Note that I am checking if it is blank since it will add blank items when it is autocleared.
If(!IsBlank(First(Self.SelectedItems)), Collect(colTaggedPeople,First(Self.SelectedItems)));
Reset(ComboboxCanvas1);
When the user hits the send button, I am patching the comment to the comments table (putting it inside of a Set() statement which returns the patched item to the varNewComment so that we can reference it when patching the tags. I had initially intended to loop through all of the colTaggedPeople to patch them to the Comment Tags table, but the platform blocked looping and patching one item at a time. So here, I am patching the first three (and if you want to allow more, just continue the Index(colTaggedPeople, 4)... 5,6, etc). I couldn't find a solution to dynamically loop and get as many as the user wants. You can see that I reset the text input, and clear the colTaggedPeople collection.
Set(varNewComment,Patch('Project Comments',Defaults('Project Comments'),{Comment:TextInput1.Text,Project:Gallery1.Selected}));
Patch('Comment Tags', Defaults('Comment Tags'),{'comment tagged person':First(colTaggedPeople),comment:varNewComment});
'PowerAppV2->Compose'.Run(First(colTaggedPeople).Mail, Gallery1.Selected.Name);
If(CountRows(colTaggedPeople) > 1,
Patch('Comment Tags', Defaults('Comment Tags'),{'comment tagged person':Index(colTaggedPeople,2),comment:varNewComment});
If(CountRows(colTaggedPeople)>2,Patch('Comment Tags', Defaults('Comment Tags'),{'comment tagged person':Index(colTaggedPeople,3),comment:varNewComment}));
);
Reset(TextInput1);
Clear(colTaggedPeople);
For replies, I am using a popup window:
I have a second collection for varTaggedReplyPeople which get added to with the same method as above with the Comment tagging.
Then the patch looks pretty much the same with the Replies, but adding the reference to the Comment.
Set(varCurrentReplyItem,Patch('Comment Replies',Defaults('Comment Replies'),{'Reply Text': TextInputCanvas1.Value,Comment:varCurrentItem}));
Patch('Reply Tags', Defaults('Reply Tags'),{'Reply Tagged Person':First(colTaggedReplyPeople),Reply:varCurrentReplyItem});
If(CountRows(colTaggedReplyPeople) > 1,
Patch('Reply Tags', Defaults('Reply Tags'),{'Reply Tagged Person':Index(colTaggedReplyPeople,2),Reply:varCurrentReplyItem});
If(CountRows(colTaggedReplyPeople)>2, Patch('Reply Tags', Defaults('Reply Tags'),{'Reply Tagged Person':Index(colTaggedReplyPeople,3),Reply:varCurrentReplyItem}));
);
Reset(TextInputCanvas1);
Set(varShowReplyBox,false);
I created the reply popup with a regular container with its visible property set to a variable which gets set to true when the reply button is pressed and set to false at the end of the patch statement (or when cancel is clicked).
Just a note, set your comment fields to Autoheight=true. This will help with sizing the gallery rows. Also I have struggled to get the spacing to work well. In one version I set the main gallery's TemplateSize dynamically to the height of the comment text field plus the reply gallery's height plus a bit for the tags (in my case 150). I also created a version where all of the sizes were set statically which made for a more stable visual experience, but there were some downsides including more space between things than I wanted. So I suggest playing around with these and see what works best. Also note that when the app is in editing mode, the galleries can look pretty crazy and overlapping, but when it is played it straightens its self up. This aspect has taken the most tweaking.