Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

[APP-834] Allow @ing someone in post directly from profile (#1241)

* setup `initMention` for mobile

* setup creating post with profile tagged on web

authored by

Ansh and committed by
GitHub
16b265a8 3aadc43c

+99 -5
+1
src/state/models/ui/shell.ts
··· 232 232 replyTo?: ComposerOptsPostRef 233 233 onPost?: () => void 234 234 quote?: ComposerOptsQuote 235 + mention?: string // handle of user to mention 235 236 } 236 237 237 238 export class ShellUiModel {
+13 -1
src/view/com/composer/Composer.tsx
··· 45 45 import {MAX_GRAPHEME_LENGTH} from 'lib/constants' 46 46 import {LabelsBtn} from './labels/LabelsBtn' 47 47 import {SelectLangBtn} from './select-language/SelectLangBtn' 48 + import {insertMentionAt} from 'lib/strings/mention-manip' 48 49 49 50 type Props = ComposerOpts & { 50 51 onClose: () => void ··· 55 56 onPost, 56 57 onClose, 57 58 quote: initQuote, 59 + mention: initMention, 58 60 }: Props) { 59 61 const {track} = useAnalytics() 60 62 const pal = usePalette('default') ··· 64 66 const [isProcessing, setIsProcessing] = useState(false) 65 67 const [processingState, setProcessingState] = useState('') 66 68 const [error, setError] = useState('') 67 - const [richtext, setRichText] = useState(new RichText({text: ''})) 69 + const [richtext, setRichText] = useState( 70 + new RichText({ 71 + text: initMention 72 + ? insertMentionAt( 73 + `@${initMention}`, 74 + initMention.length + 1, 75 + `${initMention}`, 76 + ) // insert mention if passed in 77 + : '', 78 + }), 79 + ) 68 80 const graphemeLength = useMemo(() => { 69 81 return shortenLinks(richtext).graphemeLength 70 82 }, [richtext])
+59 -1
src/view/com/composer/text-input/TextInput.web.tsx
··· 114 114 } 115 115 }, 116 116 }, 117 - content: richtext.text.toString(), 117 + content: textToEditorJson(richtext.text.toString()), 118 + onFocus: ({editor: e}) => { 119 + e.chain().focus().setTextSelection(richtext.text.length).run() // focus to the end of the text 120 + }, 118 121 autofocus: true, 119 122 editable: true, 120 123 injectCSS: true, ··· 164 167 text += `@${json.attrs?.id || ''}` 165 168 } 166 169 return text 170 + } 171 + 172 + function textToEditorJson(text: string): JSONContent { 173 + if (text === '' || text.length === 0) { 174 + return { 175 + text: '', 176 + } 177 + } 178 + 179 + const lines = text.split('\n') 180 + const docContent: JSONContent[] = [] 181 + 182 + for (const line of lines) { 183 + if (line.trim() === '') { 184 + continue // skip empty lines 185 + } 186 + 187 + const paragraphContent: JSONContent[] = [] 188 + let position = 0 189 + 190 + while (position < line.length) { 191 + if (line[position] === '@') { 192 + // Handle mentions 193 + let endPosition = position + 1 194 + while (endPosition < line.length && /\S/.test(line[endPosition])) { 195 + endPosition++ 196 + } 197 + const mentionId = line.substring(position + 1, endPosition) 198 + paragraphContent.push({ 199 + type: 'mention', 200 + attrs: {id: mentionId}, 201 + }) 202 + position = endPosition 203 + } else { 204 + // Handle regular text 205 + let endPosition = line.indexOf('@', position) 206 + if (endPosition === -1) endPosition = line.length 207 + paragraphContent.push({ 208 + type: 'text', 209 + text: line.substring(position, endPosition), 210 + }) 211 + position = endPosition 212 + } 213 + } 214 + 215 + docContent.push({ 216 + type: 'paragraph', 217 + content: paragraphContent, 218 + }) 219 + } 220 + 221 + return { 222 + type: 'doc', 223 + content: docContent, 224 + } 167 225 } 168 226 169 227 function editorJsonToLinks(json: JSONContent): string[] {
+2 -2
src/view/screens/Profile.tsx
··· 92 92 93 93 const onPressCompose = React.useCallback(() => { 94 94 track('ProfileScreen:PressCompose') 95 - store.shell.openComposer({}) 96 - }, [store, track]) 95 + store.shell.openComposer({mention: uiState.profile.handle}) 96 + }, [store, track, uiState]) 97 97 const onSelectView = React.useCallback( 98 98 (index: number) => { 99 99 uiState.setSelectedViewIndex(index)
+3
src/view/shell/Composer.tsx
··· 14 14 onPost, 15 15 onClose, 16 16 quote, 17 + mention, 17 18 }: { 18 19 active: boolean 19 20 winHeight: number ··· 21 22 onPost?: ComposerOpts['onPost'] 22 23 onClose: () => void 23 24 quote?: ComposerOpts['quote'] 25 + mention?: ComposerOpts['mention'] 24 26 }) => { 25 27 const pal = usePalette('default') 26 28 const initInterp = useAnimatedValue(0) ··· 65 67 onPost={onPost} 66 68 onClose={onClose} 67 69 quote={quote} 70 + mention={mention} 68 71 /> 69 72 </Animated.View> 70 73 )
+3
src/view/shell/Composer.web.tsx
··· 15 15 quote, 16 16 onPost, 17 17 onClose, 18 + mention, 18 19 }: { 19 20 active: boolean 20 21 winHeight: number ··· 22 23 quote: ComposerOpts['quote'] 23 24 onPost?: ComposerOpts['onPost'] 24 25 onClose: () => void 26 + mention?: ComposerOpts['mention'] 25 27 }) => { 26 28 const pal = usePalette('default') 27 29 ··· 40 42 quote={quote} 41 43 onPost={onPost} 42 44 onClose={onClose} 45 + mention={mention} 43 46 /> 44 47 </View> 45 48 </View>
+16 -1
src/view/shell/desktop/LeftNav.tsx
··· 150 150 151 151 function ComposeBtn() { 152 152 const store = useStores() 153 - const onPressCompose = () => store.shell.openComposer({}) 153 + const {getState} = useNavigation() 154 + 155 + const getProfileHandle = () => { 156 + const {routes} = getState() 157 + const currentRoute = routes[routes.length - 1] 158 + if (currentRoute.name === 'Profile') { 159 + const {name: handle} = 160 + currentRoute.params as CommonNavigatorParams['Profile'] 161 + if (handle === store.me.handle) return undefined 162 + return handle 163 + } 164 + return undefined 165 + } 166 + 167 + const onPressCompose = () => 168 + store.shell.openComposer({mention: getProfileHandle()}) 154 169 155 170 return ( 156 171 <TouchableOpacity
+1
src/view/shell/index.tsx
··· 68 68 replyTo={store.shell.composerOpts?.replyTo} 69 69 onPost={store.shell.composerOpts?.onPost} 70 70 quote={store.shell.composerOpts?.quote} 71 + mention={store.shell.composerOpts?.mention} 71 72 /> 72 73 <ModalsContainer /> 73 74 <Lightbox />
+1
src/view/shell/index.web.tsx
··· 49 49 replyTo={store.shell.composerOpts?.replyTo} 50 50 quote={store.shell.composerOpts?.quote} 51 51 onPost={store.shell.composerOpts?.onPost} 52 + mention={store.shell.composerOpts?.mention} 52 53 /> 53 54 {!isDesktop && <BottomBarWeb />} 54 55 <ModalsContainer />