Bluesky app fork with some witchin' additions 💫
at main 96 lines 2.5 kB view raw
1import {useCallback, useMemo} from 'react' 2import {type RichText} from '@atproto/api' 3import Graphemer from 'graphemer' 4 5import {shortenLinks} from './rich-text-manip' 6 7export function enforceLen( 8 str: string, 9 len: number, 10 ellipsis = false, 11 mode: 'end' | 'middle' = 'end', 12): string { 13 str = str || '' 14 if (str.length > len) { 15 if (ellipsis) { 16 if (mode === 'end') { 17 return str.slice(0, len) + '…' 18 } else if (mode === 'middle') { 19 const half = Math.floor(len / 2) 20 return str.slice(0, half) + '…' + str.slice(-half) 21 } else { 22 // fallback 23 return str.slice(0, len) 24 } 25 } else { 26 return str.slice(0, len) 27 } 28 } 29 return str 30} 31 32export function useEnforceMaxGraphemeCount() { 33 const splitter = useMemo(() => new Graphemer(), []) 34 35 return useCallback( 36 (text: string, maxCount: number) => { 37 if (splitter.countGraphemes(text) > maxCount) { 38 return splitter.splitGraphemes(text).slice(0, maxCount).join('') 39 } else { 40 return text 41 } 42 }, 43 [splitter], 44 ) 45} 46 47export function useWarnMaxGraphemeCount({ 48 text, 49 maxCount, 50}: { 51 text: string | RichText 52 maxCount: number 53}) { 54 const splitter = useMemo(() => new Graphemer(), []) 55 56 return useMemo(() => { 57 if (typeof text === 'string') { 58 return splitter.countGraphemes(text) > maxCount 59 } else { 60 return shortenLinks(text).graphemeLength > maxCount 61 } 62 }, [splitter, maxCount, text]) 63} 64 65export function countLines(str: string | undefined): number { 66 if (!str) return 0 67 return str.match(/\n/g)?.length ?? 0 68} 69 70// Augments search query with additional syntax like `from:me` 71export function augmentSearchQuery(query: string, {did}: {did?: string}) { 72 // Don't do anything if there's no DID 73 if (!did) { 74 return query 75 } 76 77 // replace “smart quotes” with normal ones 78 // iOS keyboard will add fancy unicode quotes, but only normal ones work 79 query = query.replaceAll(/[“”]/g, '"') 80 81 // We don't want to replace substrings that are being "quoted" because those 82 // are exact string matches, so what we'll do here is to split them apart 83 84 // Even-indexed strings are unquoted, odd-indexed strings are quoted 85 const splits = query.split(/("(?:[^"\\]|\\.)*")/g) 86 87 return splits 88 .map((str, idx) => { 89 if (idx % 2 === 0) { 90 return str.replaceAll(/(^|\s)from:me(\s|$)/g, `$1${did}$2`) 91 } 92 93 return str 94 }) 95 .join('') 96}