Changeset View
Changeset View
Standalone View
Standalone View
lib/shared/mention-utils.js
// @flow | // @flow | ||||
import type { RelativeMemberInfo } from '../types/thread-types'; | import type { RelativeMemberInfo } from '../types/thread-types'; | ||||
import { oldValidUsernameRegexString } from './account-utils'; | import { oldValidUsernameRegexString } from './account-utils'; | ||||
import SearchIndex from './search-index'; | import SearchIndex from './search-index'; | ||||
import { stringForUserExplicit } from './user-utils'; | import { stringForUserExplicit } from './user-utils'; | ||||
const mentionRegex: RegExp = new RegExp( | |||||
`(?<textPrefix>(?:^(?:.|\n)*\\s+)|^)@(?<username>${oldValidUsernameRegexString})?$`, | |||||
tomek: At this point I can see that there might be two issues with it:
1. `(?:^(?:.|\n)*\\s+)|^` - It… | |||||
przemekAuthorUnsubmitted Done Inline Actions
We might consider doing some benchmarks and maybe optimise this Regex, but my understanding is they are pretty efficient anyway.
przemek: @tomek
1. That part is needed so we only detect the last @ with some name prefix. In mine… | |||||
); | |||||
function getTypeaheadUserSuggestions( | function getTypeaheadUserSuggestions( | ||||
userSearchIndex: SearchIndex, | userSearchIndex: SearchIndex, | ||||
usersInThread: $ReadOnlyArray<RelativeMemberInfo>, | usersInThread: $ReadOnlyArray<RelativeMemberInfo>, | ||||
typedPrefix: string, | typedPrefix: string, | ||||
): $ReadOnlyArray<RelativeMemberInfo> { | ): $ReadOnlyArray<RelativeMemberInfo> { | ||||
const userIDs = userSearchIndex.getSearchResults(typedPrefix); | const userIDs = userSearchIndex.getSearchResults(typedPrefix); | ||||
return usersInThread | return usersInThread | ||||
.filter( | .filter(user => typedPrefix.length === 0 || userIDs.includes(user.id)) | ||||
(user: RelativeMemberInfo) => | |||||
typedPrefix.length === 0 || userIDs.includes(user.id), | |||||
) | |||||
.sort((userA, userB) => | .sort((userA, userB) => | ||||
stringForUserExplicit(userA).localeCompare(stringForUserExplicit(userB)), | stringForUserExplicit(userA).localeCompare(stringForUserExplicit(userB)), | ||||
); | ); | ||||
} | } | ||||
const mentionRegex: RegExp = new RegExp( | function getTextOffsets( | ||||
`(^|.* )@(${oldValidUsernameRegexString})?$`, | textarea: ?HTMLTextAreaElement, | ||||
text: string, | |||||
): { topTextOffset: number, leftTextOffset: number } { | |||||
if (!textarea) { | |||||
return { topTextOffset: 0, leftTextOffset: 0 }; | |||||
} | |||||
// terribly hacky but it works I guess :D | |||||
// we had to use it, as it's hard to count lines in textarea | |||||
// and track cursor position within it as | |||||
// lines can be wrapped into new lines without \n character | |||||
// as result of overflow | |||||
const textareaStyle: CSSStyleDeclaration = window.getComputedStyle( | |||||
textarea, | |||||
null, | |||||
); | ); | ||||
const div = document.createElement('div'); | |||||
ashoatUnsubmitted Not Done Inline ActionsCan you link to where you got this solution from? And can you link to a recent source that asserts this is the modern way to do this? ashoat: Can you link to where you got this solution from? And can you link to a recent source that… | |||||
for (const styleName of textareaStyle) { | |||||
div.style.setProperty(styleName, textareaStyle.getPropertyValue(styleName)); | |||||
} | |||||
ashoatUnsubmitted Not Done Inline ActionsDo we need to do this for literally every style? ashoat: Do we need to do this for literally every style? | |||||
div.style.display = 'inline-block'; | |||||
div.style.position = 'absolute'; | |||||
div.textContent = text; | |||||
const span = document.createElement('span'); | |||||
span.textContent = textarea.value.slice(text.length); | |||||
div.appendChild(span); | |||||
document.body?.appendChild(div); | |||||
const { offsetTop, offsetLeft } = span; | |||||
document.body?.removeChild(div); | |||||
return { | |||||
topTextOffset: offsetTop - textarea.scrollTop, | |||||
tomekUnsubmitted Done Inline ActionsHave you tested a case where textarea contains huge amount of text (more than full screen of it)? tomek: Have you tested a case where textarea contains huge amount of text (more than full screen of… | |||||
przemekAuthorUnsubmitted Done Inline ActionsYeah, sure, it work as expected. przemek: Yeah, sure, it work as expected. | |||||
leftTextOffset: offsetLeft, | |||||
}; | |||||
} | |||||
export { getTypeaheadUserSuggestions, mentionRegex }; | export { mentionRegex, getTypeaheadUserSuggestions, getTextOffsets }; |
At this point I can see that there might be two issues with it: