[APP-834] Allow @ing someone in post directly from profile (#1241)
* setup `initMention` for mobile * setup creating post with profile tagged on webzio/stable
parent
3aadc43c89
commit
16b265a861
|
@ -232,6 +232,7 @@ export interface ComposerOpts {
|
||||||
replyTo?: ComposerOptsPostRef
|
replyTo?: ComposerOptsPostRef
|
||||||
onPost?: () => void
|
onPost?: () => void
|
||||||
quote?: ComposerOptsQuote
|
quote?: ComposerOptsQuote
|
||||||
|
mention?: string // handle of user to mention
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ShellUiModel {
|
export class ShellUiModel {
|
||||||
|
|
|
@ -45,6 +45,7 @@ import {Gallery} from './photos/Gallery'
|
||||||
import {MAX_GRAPHEME_LENGTH} from 'lib/constants'
|
import {MAX_GRAPHEME_LENGTH} from 'lib/constants'
|
||||||
import {LabelsBtn} from './labels/LabelsBtn'
|
import {LabelsBtn} from './labels/LabelsBtn'
|
||||||
import {SelectLangBtn} from './select-language/SelectLangBtn'
|
import {SelectLangBtn} from './select-language/SelectLangBtn'
|
||||||
|
import {insertMentionAt} from 'lib/strings/mention-manip'
|
||||||
|
|
||||||
type Props = ComposerOpts & {
|
type Props = ComposerOpts & {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
|
@ -55,6 +56,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
onPost,
|
onPost,
|
||||||
onClose,
|
onClose,
|
||||||
quote: initQuote,
|
quote: initQuote,
|
||||||
|
mention: initMention,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const {track} = useAnalytics()
|
const {track} = useAnalytics()
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
@ -64,7 +66,17 @@ export const ComposePost = observer(function ComposePost({
|
||||||
const [isProcessing, setIsProcessing] = useState(false)
|
const [isProcessing, setIsProcessing] = useState(false)
|
||||||
const [processingState, setProcessingState] = useState('')
|
const [processingState, setProcessingState] = useState('')
|
||||||
const [error, setError] = useState('')
|
const [error, setError] = useState('')
|
||||||
const [richtext, setRichText] = useState(new RichText({text: ''}))
|
const [richtext, setRichText] = useState(
|
||||||
|
new RichText({
|
||||||
|
text: initMention
|
||||||
|
? insertMentionAt(
|
||||||
|
`@${initMention}`,
|
||||||
|
initMention.length + 1,
|
||||||
|
`${initMention}`,
|
||||||
|
) // insert mention if passed in
|
||||||
|
: '',
|
||||||
|
}),
|
||||||
|
)
|
||||||
const graphemeLength = useMemo(() => {
|
const graphemeLength = useMemo(() => {
|
||||||
return shortenLinks(richtext).graphemeLength
|
return shortenLinks(richtext).graphemeLength
|
||||||
}, [richtext])
|
}, [richtext])
|
||||||
|
|
|
@ -114,7 +114,10 @@ export const TextInput = React.forwardRef(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
content: richtext.text.toString(),
|
content: textToEditorJson(richtext.text.toString()),
|
||||||
|
onFocus: ({editor: e}) => {
|
||||||
|
e.chain().focus().setTextSelection(richtext.text.length).run() // focus to the end of the text
|
||||||
|
},
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
editable: true,
|
editable: true,
|
||||||
injectCSS: true,
|
injectCSS: true,
|
||||||
|
@ -166,6 +169,61 @@ function editorJsonToText(json: JSONContent): string {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function textToEditorJson(text: string): JSONContent {
|
||||||
|
if (text === '' || text.length === 0) {
|
||||||
|
return {
|
||||||
|
text: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = text.split('\n')
|
||||||
|
const docContent: JSONContent[] = []
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.trim() === '') {
|
||||||
|
continue // skip empty lines
|
||||||
|
}
|
||||||
|
|
||||||
|
const paragraphContent: JSONContent[] = []
|
||||||
|
let position = 0
|
||||||
|
|
||||||
|
while (position < line.length) {
|
||||||
|
if (line[position] === '@') {
|
||||||
|
// Handle mentions
|
||||||
|
let endPosition = position + 1
|
||||||
|
while (endPosition < line.length && /\S/.test(line[endPosition])) {
|
||||||
|
endPosition++
|
||||||
|
}
|
||||||
|
const mentionId = line.substring(position + 1, endPosition)
|
||||||
|
paragraphContent.push({
|
||||||
|
type: 'mention',
|
||||||
|
attrs: {id: mentionId},
|
||||||
|
})
|
||||||
|
position = endPosition
|
||||||
|
} else {
|
||||||
|
// Handle regular text
|
||||||
|
let endPosition = line.indexOf('@', position)
|
||||||
|
if (endPosition === -1) endPosition = line.length
|
||||||
|
paragraphContent.push({
|
||||||
|
type: 'text',
|
||||||
|
text: line.substring(position, endPosition),
|
||||||
|
})
|
||||||
|
position = endPosition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
docContent.push({
|
||||||
|
type: 'paragraph',
|
||||||
|
content: paragraphContent,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: 'doc',
|
||||||
|
content: docContent,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function editorJsonToLinks(json: JSONContent): string[] {
|
function editorJsonToLinks(json: JSONContent): string[] {
|
||||||
let links: string[] = []
|
let links: string[] = []
|
||||||
if (json.content?.length) {
|
if (json.content?.length) {
|
||||||
|
|
|
@ -92,8 +92,8 @@ export const ProfileScreen = withAuthRequired(
|
||||||
|
|
||||||
const onPressCompose = React.useCallback(() => {
|
const onPressCompose = React.useCallback(() => {
|
||||||
track('ProfileScreen:PressCompose')
|
track('ProfileScreen:PressCompose')
|
||||||
store.shell.openComposer({})
|
store.shell.openComposer({mention: uiState.profile.handle})
|
||||||
}, [store, track])
|
}, [store, track, uiState])
|
||||||
const onSelectView = React.useCallback(
|
const onSelectView = React.useCallback(
|
||||||
(index: number) => {
|
(index: number) => {
|
||||||
uiState.setSelectedViewIndex(index)
|
uiState.setSelectedViewIndex(index)
|
||||||
|
|
|
@ -14,6 +14,7 @@ export const Composer = observer(
|
||||||
onPost,
|
onPost,
|
||||||
onClose,
|
onClose,
|
||||||
quote,
|
quote,
|
||||||
|
mention,
|
||||||
}: {
|
}: {
|
||||||
active: boolean
|
active: boolean
|
||||||
winHeight: number
|
winHeight: number
|
||||||
|
@ -21,6 +22,7 @@ export const Composer = observer(
|
||||||
onPost?: ComposerOpts['onPost']
|
onPost?: ComposerOpts['onPost']
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
quote?: ComposerOpts['quote']
|
quote?: ComposerOpts['quote']
|
||||||
|
mention?: ComposerOpts['mention']
|
||||||
}) => {
|
}) => {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
const initInterp = useAnimatedValue(0)
|
const initInterp = useAnimatedValue(0)
|
||||||
|
@ -65,6 +67,7 @@ export const Composer = observer(
|
||||||
onPost={onPost}
|
onPost={onPost}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
quote={quote}
|
quote={quote}
|
||||||
|
mention={mention}
|
||||||
/>
|
/>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,6 +15,7 @@ export const Composer = observer(
|
||||||
quote,
|
quote,
|
||||||
onPost,
|
onPost,
|
||||||
onClose,
|
onClose,
|
||||||
|
mention,
|
||||||
}: {
|
}: {
|
||||||
active: boolean
|
active: boolean
|
||||||
winHeight: number
|
winHeight: number
|
||||||
|
@ -22,6 +23,7 @@ export const Composer = observer(
|
||||||
quote: ComposerOpts['quote']
|
quote: ComposerOpts['quote']
|
||||||
onPost?: ComposerOpts['onPost']
|
onPost?: ComposerOpts['onPost']
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
|
mention?: ComposerOpts['mention']
|
||||||
}) => {
|
}) => {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
|
||||||
|
@ -40,6 +42,7 @@ export const Composer = observer(
|
||||||
quote={quote}
|
quote={quote}
|
||||||
onPost={onPost}
|
onPost={onPost}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
mention={mention}
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
@ -150,7 +150,22 @@ const NavItem = observer(
|
||||||
|
|
||||||
function ComposeBtn() {
|
function ComposeBtn() {
|
||||||
const store = useStores()
|
const store = useStores()
|
||||||
const onPressCompose = () => store.shell.openComposer({})
|
const {getState} = useNavigation()
|
||||||
|
|
||||||
|
const getProfileHandle = () => {
|
||||||
|
const {routes} = getState()
|
||||||
|
const currentRoute = routes[routes.length - 1]
|
||||||
|
if (currentRoute.name === 'Profile') {
|
||||||
|
const {name: handle} =
|
||||||
|
currentRoute.params as CommonNavigatorParams['Profile']
|
||||||
|
if (handle === store.me.handle) return undefined
|
||||||
|
return handle
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPressCompose = () =>
|
||||||
|
store.shell.openComposer({mention: getProfileHandle()})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
|
|
|
@ -68,6 +68,7 @@ const ShellInner = observer(() => {
|
||||||
replyTo={store.shell.composerOpts?.replyTo}
|
replyTo={store.shell.composerOpts?.replyTo}
|
||||||
onPost={store.shell.composerOpts?.onPost}
|
onPost={store.shell.composerOpts?.onPost}
|
||||||
quote={store.shell.composerOpts?.quote}
|
quote={store.shell.composerOpts?.quote}
|
||||||
|
mention={store.shell.composerOpts?.mention}
|
||||||
/>
|
/>
|
||||||
<ModalsContainer />
|
<ModalsContainer />
|
||||||
<Lightbox />
|
<Lightbox />
|
||||||
|
|
|
@ -49,6 +49,7 @@ const ShellInner = observer(() => {
|
||||||
replyTo={store.shell.composerOpts?.replyTo}
|
replyTo={store.shell.composerOpts?.replyTo}
|
||||||
quote={store.shell.composerOpts?.quote}
|
quote={store.shell.composerOpts?.quote}
|
||||||
onPost={store.shell.composerOpts?.onPost}
|
onPost={store.shell.composerOpts?.onPost}
|
||||||
|
mention={store.shell.composerOpts?.mention}
|
||||||
/>
|
/>
|
||||||
{!isDesktop && <BottomBarWeb />}
|
{!isDesktop && <BottomBarWeb />}
|
||||||
<ModalsContainer />
|
<ModalsContainer />
|
||||||
|
|
Loading…
Reference in New Issue