diff --git a/src/lib/strings/hashtags.ts b/src/lib/strings/hashtags.ts new file mode 100644 index 0000000000..eb510f4d3b --- /dev/null +++ b/src/lib/strings/hashtags.ts @@ -0,0 +1,3 @@ +export const TAG_REGEX = /(?:^|\s)(#[^\d\s]\S*)(?=\s)?/gi +export const ENDING_PUNCTUATION_REGEX = /\p{P}+$/gu +export const LEADING_HASH_REGEX = /^#/ diff --git a/src/view/com/composer/TagInput/index.tsx b/src/view/com/composer/TagInput/index.tsx index afd6569cb0..586d6b1f8c 100644 --- a/src/view/com/composer/TagInput/index.tsx +++ b/src/view/com/composer/TagInput/index.tsx @@ -22,6 +22,10 @@ import * as Sheet from 'view/com/sheets/Base' import {useStores} from 'state/index' import {ActivityIndicator} from 'react-native' import {TagInputEntryButton} from './TagInputEntryButton' +import { + ENDING_PUNCTUATION_REGEX, + LEADING_HASH_REGEX, +} from 'lib/strings/hashtags' function uniq(tags: string[]) { return Array.from(new Set(tags)) @@ -30,8 +34,8 @@ function uniq(tags: string[]) { function sanitize(tagString: string) { return tagString .trim() - .replace(/^#/, '') - .replace(/\p{P}+$/gu, '') + .replace(LEADING_HASH_REGEX, '') + .replace(ENDING_PUNCTUATION_REGEX, '') } export function TagInput({ diff --git a/src/view/com/composer/TagInput/index.web.tsx b/src/view/com/composer/TagInput/index.web.tsx index ffaf04a057..0b5c332bb4 100644 --- a/src/view/com/composer/TagInput/index.web.tsx +++ b/src/view/com/composer/TagInput/index.web.tsx @@ -14,6 +14,10 @@ import { } from '@fortawesome/react-native-fontawesome' import {Pin} from 'pind' +import { + ENDING_PUNCTUATION_REGEX, + LEADING_HASH_REGEX, +} from 'lib/strings/hashtags' import {isWeb} from 'platform/detection' import {TagsAutocompleteModel} from 'state/models/ui/tags-autocomplete' import {usePalette} from 'lib/hooks/usePalette' @@ -30,8 +34,8 @@ function uniq(tags: string[]) { function sanitize(tagString: string) { return tagString .trim() - .replace(/^#/, '') - .replace(/\p{P}+$/gu, '') + .replace(LEADING_HASH_REGEX, '') + .replace(ENDING_PUNCTUATION_REGEX, '') } export function TagInput({ diff --git a/src/view/com/composer/text-input/mobile/TagsAutocomplete.tsx b/src/view/com/composer/text-input/mobile/TagsAutocomplete.tsx index e7512796d9..da6ca83477 100644 --- a/src/view/com/composer/text-input/mobile/TagsAutocomplete.tsx +++ b/src/view/com/composer/text-input/mobile/TagsAutocomplete.tsx @@ -5,12 +5,11 @@ import {TagsAutocompleteModel} from 'state/models/ui/tags-autocomplete' import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' import {usePalette} from 'lib/hooks/usePalette' import {Text} from 'view/com/util/text/Text' +import {LEADING_HASH_REGEX, TAG_REGEX} from 'lib/strings/hashtags' export function getHashtagAt(text: string, position: number) { - const regex = /(?:^|\s)(#[^\d\s]\S*)(?=\s)?/gi - let match - while ((match = regex.exec(text))) { + while ((match = TAG_REGEX.exec(text))) { const [matchedString, tag] = match if (tag.length > 66) continue @@ -27,7 +26,7 @@ export function getHashtagAt(text: string, position: number) { * show autocomplete after a single # is typed * AND the cursor is next to the # */ - const hashRegex = /#/g + const hashRegex = LEADING_HASH_REGEX let hashMatch while ((hashMatch = hashRegex.exec(text))) { if (position >= hashMatch.index && position <= hashMatch.index + 1) { diff --git a/src/view/com/composer/text-input/web/Tags/utils.ts b/src/view/com/composer/text-input/web/Tags/utils.ts index ec2a6b2047..81e6ee4b6b 100644 --- a/src/view/com/composer/text-input/web/Tags/utils.ts +++ b/src/view/com/composer/text-input/web/Tags/utils.ts @@ -1,5 +1,11 @@ +import { + TAG_REGEX, + ENDING_PUNCTUATION_REGEX, + LEADING_HASH_REGEX, +} from 'lib/strings/hashtags' + export function parsePunctuationFromTag(value: string) { - const reg = /(\p{P}+)$/gu + const reg = ENDING_PUNCTUATION_REGEX const tag = value.replace(reg, '') const punctuation = value.match(reg)?.[0] || '' @@ -13,9 +19,7 @@ export function findSuggestionMatch({ text: string cursorPosition: number }) { - const regex = /(?:^|\s)(#[^\d\s]\S*)(?=\s)?/g - const puncRegex = /\p{P}+$/gu - const match = Array.from(text.matchAll(regex)).pop() + const match = Array.from(text.matchAll(TAG_REGEX)).pop() if (!match || match.input === undefined || match.index === undefined) { return null @@ -24,7 +28,9 @@ export function findSuggestionMatch({ const startIndex = cursorPosition - text.length let [matchedString, tag] = match - const sanitized = tag.replace(puncRegex, '').replace(/^#/, '') + const sanitized = tag + .replace(ENDING_PUNCTUATION_REGEX, '') + .replace(LEADING_HASH_REGEX, '') // one of our hashtag spec rules if (sanitized.length > 64) return null @@ -45,7 +51,7 @@ export function findSuggestionMatch({ * We parse out the punctuation later, but we don't want to pass * the # to the search query. */ - query: tag.replace(/^#/, ''), + query: tag.replace(LEADING_HASH_REGEX, ''), // raw text string text: matchedString, }