parse-mentions.ts 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. import { UserId } from '../../../../../types/user'
  2. export type MentionSegment =
  3. | { type: 'text'; value: string }
  4. | { type: 'mention'; userId: UserId }
  5. const MENTION_REGEX = /@\[([a-f0-9]{24})\]/g
  6. export function parseMentions(content: string): MentionSegment[] {
  7. const segments: MentionSegment[] = []
  8. let lastIndex = 0
  9. for (const match of content.matchAll(MENTION_REGEX)) {
  10. const matchStart = match.index
  11. if (matchStart > lastIndex) {
  12. segments.push({
  13. type: 'text',
  14. value: content.slice(lastIndex, matchStart),
  15. })
  16. }
  17. segments.push({ type: 'mention', userId: match[1] as UserId })
  18. lastIndex = matchStart + match[0].length
  19. }
  20. if (lastIndex < content.length) {
  21. segments.push({ type: 'text', value: content.slice(lastIndex) })
  22. }
  23. return segments
  24. }
  25. const MENTION_RAW_LENGTH = 27 // @[ + 24 hex chars + ]
  26. export function sliceMentionSegments(
  27. segments: MentionSegment[],
  28. limit: number
  29. ): MentionSegment[] {
  30. const result: MentionSegment[] = []
  31. let consumed = 0
  32. for (const segment of segments) {
  33. if (consumed >= limit) break
  34. const length =
  35. segment.type === 'text' ? segment.value.length : MENTION_RAW_LENGTH
  36. const remaining = limit - consumed
  37. if (length <= remaining) {
  38. result.push(segment)
  39. consumed += length
  40. continue
  41. }
  42. if (segment.type === 'text') {
  43. result.push({ type: 'text', value: segment.value.slice(0, remaining) })
  44. }
  45. break
  46. }
  47. return result
  48. }