diff --git a/src/lib/strings/hashtags.ts b/src/lib/strings/hashtags.ts index 1969718b88..3613256b13 100644 --- a/src/lib/strings/hashtags.ts +++ b/src/lib/strings/hashtags.ts @@ -1,10 +1,3 @@ -export const TAG_REGEX = - /(?:^|\s)(#[\p{L}\p{Emoji_Presentation}\p{Emoji_Modifier_Base}\p{Extended_Pictographic}]{1}[\p{L}\p{Emoji_Presentation}\p{Emoji_Modifier_Base}\p{Extended_Pictographic}\d_-]*)/giu -export const LOOSE_TAG_REGEX = - /(?:^|\s)(#[\p{L}\p{Emoji_Presentation}\p{Emoji_Modifier_Base}\p{Extended_Pictographic}]{1}[\p{L}\p{Emoji_Presentation}\p{Emoji_Modifier_Base}\p{Extended_Pictographic}\d_-]*\S*)/giu -export const ENDING_PUNCTUATION_REGEX = /\p{P}+$/gu -export const LEADING_HASH_REGEX = /^#/g - export function sanitize(tagString: string) { return tagString .trim() diff --git a/src/state/models/ui/tags-autocomplete.ts b/src/state/models/ui/tags-autocomplete.ts index 441f04036a..e6f2a27ead 100644 --- a/src/state/models/ui/tags-autocomplete.ts +++ b/src/state/models/ui/tags-autocomplete.ts @@ -44,7 +44,7 @@ export class TagsAutocompleteModel { isActive = false query = '' searchedTags: string[] = [] - profileTags: string[] = ['biology'] + profileTags: string[] = [] constructor(public rootStore: RootStoreModel) { makeAutoObservable( @@ -113,15 +113,7 @@ export class TagsAutocompleteModel { // TODO hook up to search type-ahead async _search() { runInAction(() => { - this.searchedTags = [ - 'bluesky', - 'code', - 'coding', - 'dev', - 'developer', - 'development', - 'devlife', - ] + this.searchedTags = [] }) } } diff --git a/src/view/com/composer/TagInput/index.tsx b/src/view/com/composer/TagInput/index.tsx index de637ce61a..a01b94152c 100644 --- a/src/view/com/composer/TagInput/index.tsx +++ b/src/view/com/composer/TagInput/index.tsx @@ -22,8 +22,8 @@ import * as Sheet from 'view/com/sheets/Base' import {useStores} from 'state/index' import {ActivityIndicator} from 'react-native' import {TagInputEntryButton} from './TagInputEntryButton' -import {sanitize} from 'lib/strings/hashtags' import {uniq} from 'lib/strings/helpers' +import {sanitizeHashtag} from './util' export function TagInput({ max = 8, @@ -70,7 +70,7 @@ export function TagInput({ const addTagAndReset = React.useCallback( (value: string) => { - const tag = sanitize(value) + const tag = sanitizeHashtag(value) // enforce max hashtag length if (tag.length > 0 && tag.length <= 64) { diff --git a/src/view/com/composer/TagInput/index.web.tsx b/src/view/com/composer/TagInput/index.web.tsx index 5180ad8d1f..d0ac8d2c75 100644 --- a/src/view/com/composer/TagInput/index.web.tsx +++ b/src/view/com/composer/TagInput/index.web.tsx @@ -22,8 +22,8 @@ import {Text} from 'view/com/util/text/Text' import {useStores} from 'state/index' import {TagInputEntryButton} from './TagInputEntryButton' import {TextInputFocusEventData} from 'react-native' -import {sanitize} from 'lib/strings/hashtags' import {uniq} from 'lib/strings/helpers' +import {sanitizeHashtag} from './util' export function TagInput({ max = 8, @@ -84,7 +84,7 @@ export function TagInput({ const addTagAndReset = React.useCallback( (value: string) => { - const tag = sanitize(value) + const tag = sanitizeHashtag(value) // enforce max hashtag length if (tag.length > 0 && tag.length <= 64) { diff --git a/src/view/com/composer/TagInput/util.ts b/src/view/com/composer/TagInput/util.ts new file mode 100644 index 0000000000..b085054853 --- /dev/null +++ b/src/view/com/composer/TagInput/util.ts @@ -0,0 +1,8 @@ +import {TRAILING_PUNCTUATION_REGEX, LEADING_HASH_REGEX} from '@atproto/api' + +export function sanitizeHashtag(tagString: string) { + return tagString + .trim() + .replace(LEADING_HASH_REGEX, '') + .replace(TRAILING_PUNCTUATION_REGEX, '') +} diff --git a/src/view/com/composer/text-input/mobile/TagsAutocomplete.tsx b/src/view/com/composer/text-input/mobile/TagsAutocomplete.tsx index d5d9218192..816e9509ea 100644 --- a/src/view/com/composer/text-input/mobile/TagsAutocomplete.tsx +++ b/src/view/com/composer/text-input/mobile/TagsAutocomplete.tsx @@ -5,13 +5,13 @@ 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' +import {LEADING_HASH_REGEX, HASHTAG_REGEX} from '@atproto/api' /** * Loops over matches in the text to find the hashtag under the cursor. */ export function getHashtagAt(text: string, position: number) { - for (const match of Array.from(text.matchAll(TAG_REGEX))) { + for (const match of Array.from(text.matchAll(HASHTAG_REGEX))) { const {index} = match const [matchedString, tag] = match 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 3e46957170..654552e07c 100644 --- a/src/view/com/composer/text-input/web/Tags/utils.ts +++ b/src/view/com/composer/text-input/web/Tags/utils.ts @@ -1,11 +1,15 @@ import { - LOOSE_TAG_REGEX, - ENDING_PUNCTUATION_REGEX, + HASHTAG_REGEX_WITH_TRAILING_PUNCTUATION, + TRAILING_PUNCTUATION_REGEX, LEADING_HASH_REGEX, -} from 'lib/strings/hashtags' +} from '@atproto/api' +/** + * This method eventually receives the `query` property from the result of + * `findSuggestionMatch` below. + */ export function parsePunctuationFromTag(value: string) { - const reg = ENDING_PUNCTUATION_REGEX + const reg = TRAILING_PUNCTUATION_REGEX const tag = value.replace(reg, '') const punctuation = value.match(reg)?.[0] || '' @@ -26,25 +30,27 @@ export function findSuggestionMatch({ text: string cursorPosition: number }) { - const match = Array.from(text.matchAll(LOOSE_TAG_REGEX)).pop() + const match = Array.from( + text.matchAll(HASHTAG_REGEX_WITH_TRAILING_PUNCTUATION), + ).pop() if (!match || match.input === undefined || match.index === undefined) { return null } const startIndex = cursorPosition - text.length - let [matchedString, looselyMatchedTag] = match + let [matchedString, tagWithTrailingPunctuation] = match - const sanitized = looselyMatchedTag - .replace(ENDING_PUNCTUATION_REGEX, '') + const sanitized = tagWithTrailingPunctuation + .replace(TRAILING_PUNCTUATION_REGEX, '') .replace(LEADING_HASH_REGEX, '') // one of our hashtag spec rules if (sanitized.length > 64) return null const from = - startIndex + match.index + matchedString.indexOf(looselyMatchedTag) - const to = from + looselyMatchedTag.length + startIndex + match.index + matchedString.indexOf(tagWithTrailingPunctuation) + const to = from + tagWithTrailingPunctuation.length if (from < cursorPosition && to >= cursorPosition) { return { @@ -58,7 +64,7 @@ export function findSuggestionMatch({ * * We parse out the punctuation later. */ - query: looselyMatchedTag.replace(LEADING_HASH_REGEX, ''), + query: tagWithTrailingPunctuation.replace(LEADING_HASH_REGEX, ''), // raw text string text: matchedString, }