-
Notifications
You must be signed in to change notification settings - Fork 204
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[PoC] Support creating mentions as part of annotation creation #6787
Conversation
Codecov ReportAttention: Patch coverage is
❌ Your patch status has failed because the patch coverage (25.00%) is below the target coverage (100.00%). You can increase the patch coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #6787 +/- ##
==========================================
- Coverage 99.43% 98.94% -0.49%
==========================================
Files 270 271 +1
Lines 10312 10379 +67
Branches 2474 2493 +19
==========================================
+ Hits 10254 10270 +16
- Misses 58 109 +51 ☔ View full report in Codecov by Sentry. |
6dc1f40
to
37cd560
Compare
@@ -35,21 +76,49 @@ export default function MarkdownView({ | |||
}); | |||
}, [markdown]); | |||
|
|||
useEffect(() => { | |||
const listenerCollection = new ListenerCollection(); | |||
const foundMentions = replaceMentionTags(content.current!, mentions); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly to how we replace links with embeds via a side effect, we replace mention tags with their rendered representation directly in the DOM.
src/sidebar/services/annotations.ts
Outdated
function mentionsToTags(text: string, authority: string): string { | ||
return text.replace(/(?:^|\s)@(\w+)(?=\s|$)/g, (match, username) => { | ||
const userid = `acct:${username}@${authority}`; | ||
return ` <a data-hyp-mention data-userid="${userid}">@${username}</a>`; | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This converts plain-text mentions into the special tag, but assumes the mention is the username. We'll have to evolve this for display-name-based mentions.
src/types/api.ts
Outdated
userid: string; | ||
username: string; | ||
link: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may not be entirely correct. For example, during my testing I was getting null
links. We'll have to clarify this with the BE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would suggest to extract the functions related to extracting and replacing mentions into a module in helpers
where they can be tested directly. Also I would default to use browser APIs for parsing and generating HTML unless we determine they can't be used for some reason. For listening for events, I suggested to try event delegation rather than adding listeners to each mention directly - unless you can see a reason we can't do that?
src/types/api.ts
Outdated
export type Mention = UserInfo & { | ||
userid: string; | ||
username: string; | ||
link: string; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does link
point to? The user's profile page? What about LMS users in future who don't have profile pages?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does link point to? The user's profile page?
Yes, that is the intention
What about LMS users in future who don't have profile pages?
The field should be nullable. If it comes as null
, then we would not render mentions as links, but something else.
Yep, this is a good suggestion, as using regular expressions proved to be problematic. For some reason I've been working on the assumption that we would have to use regular expressions, so didn't really think on an alternative, but since we are dealing with HTML elements, using the browser APIs makes a lot of sense. |
a748567
to
097f524
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This generally looks good. I had a query about handling of null
links when post-processing the rendered content for an annotation.
const listenerCollection = new ListenerCollection(); | ||
const foundMentions = renderMentionTags(content.current!, mentions); | ||
|
||
listenerCollection.add( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Was there a reason not to use onMouseEnterCapture
and onMouseLeaveCapture
on the content
div? (onMouseOver
and onMouseOut
could work as well with some other modifications I think).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The main reason is that renderMentionTags
needs to be called in a side effect, so we would have to track the result in a ref.
Defining the event handlers here simplified that, as it no longer needs to be referenced outside. But we can explore the other option.
src/sidebar/helpers/mentions.ts
Outdated
|
||
if (mention) { | ||
// If the mention exists in the list of mentions, render it as a link | ||
mentionLink.setAttribute('href', mention.link); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think you mentioned previously that link
can be null? Is that correct? If so what should happen here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, that's correct. In that case this should be rendered as a different type of tag, and highlighted by color only, but not underlined.
097f524
to
1364a97
Compare
This PoC has served its purpose. I'm closing it now and will start creating individual PRs |
This PR is used to verify some assumptions, and check that we can proceed according to what's described in the engineering design document for phase 1 https://docs.google.com/document/d/16Iv-MPwQi_LIIN6Fk-aAaflZqWkSX5bQIWUEg_MUDBI/edit
The implementation here is very rough, and individual PRs will be extracted with the actual, properly tested implementation.
This PR has been tested in combination with hypothesis/h#9279, to ensure FE and BE integrate as expected.
highlighted-at-mention-2025-02-03_16.47.17.mp4
mentions-popover-2025-02-05_12.00.56.mp4
Logic
This PR covers:
@something
in the annotation text with the special mention tag (<a data-hyp-mention data-userid="acct:something@authority">@something</a>
) right before sending it to the BE.We don't try to validate those are actual usernames client-side. Instead we let the BE validate them and "discard" those that are invalid.
This will allow us to highlight incorrect mentions to end-users, or simply render them as plain-text if desired.
span
.This PR assumes usernames are public and user-friendly. In future, we'll need to support mentioning via display name as well, for the LMS. In that case we will have to support an additional plain-text format for mentions that could be something like
@[Display name]
,@<Display name>
,@Display_name
or something in those lines.When converting that to the corresponding mention tag, we will have to get the userid somehow. Let's worry about that later.
TODO
@username
into special tag<a data-hyp-mention data-userid="acct:username@authority">@username</a>
@username
when entering annotation edit mode for an annotation with mentions