import {Editor} from '@tiptap/core' import {MIN_BIO_LENGTH} from 'common/constants' import {MAX_DESCRIPTION_LENGTH} from 'common/envs/constants' import {Profile, ProfileWithoutUser} from 'common/profiles/profile' import {richTextToString} from 'common/util/parse' import {tryCatch} from 'common/util/try-catch' import {useRouter} from 'next/router' import {useEffect, useState} from 'react' import ReactMarkdown from 'react-markdown' import {Button} from 'web/components/buttons/button' import {Col} from 'web/components/layout/col' import {Row} from 'web/components/layout/row' import {TextEditor, useTextEditor} from 'web/components/widgets/editor' import {NewTabLink} from 'web/components/widgets/new-tab-link' import {ShowMore} from 'web/components/widgets/show-more' import {updateProfile} from 'web/lib/api' import {useT} from 'web/lib/locale' import {track} from 'web/lib/service/analytics' export function BioTips({onClick}: {onClick?: () => void}) { const t = useT() const tips = t( 'profile.bio.tips_list', ` - Your core values, interests, and activities - Personality traits, what makes you unique and what you care about - Connection goals (collaborative, friendship, romantic) - Expectations and boundaries - Availability, how to contact you or start a conversation (email, social media, etc.) - Optional: romantic preferences, lifestyle habits, and conversation starters `, ) let href = '/tips-bio' const router = useRouter() if (router.pathname === '/signup') { href += '?fromSignup=true' } return (

{t( 'profile.bio.tips_intro', 'Write a clear and engaging bio to help others understand who you are and the connections you seek. Include:', )}

{tips} {t('profile.bio.tips_link', 'Read full tips for writing a high-quality bio')}
) } export function EditableBio(props: {profile: Profile; onSave: () => void; onCancel?: () => void}) { const {profile, onCancel, onSave} = props const [editor, setEditor] = useState(null) const [textLength, setTextLength] = useState(0) const t = useT() const hideButtons = textLength === 0 && !profile.bio const saveBio = async () => { if (!editor) return // console.log(editor.getText().length) const {error} = await tryCatch( updateProfile({ bio: editor.getJSON(), bio_length: editor.getText().length, }), ) if (error) { console.error(error) return } track('edited profile bio') } return ( { setEditor(e) if (e) setTextLength(e.getText().length) e?.on('update', () => { setTextLength(e.getText().length) }) }} /> {!hideButtons && ( {onCancel && ( )} )} ) } export function SignupBio(props: { onChange: (e: Editor) => void profile?: ProfileWithoutUser | undefined onClickTips?: () => void onEditor?: (editor: any) => void }) { const {onChange, profile, onClickTips, onEditor} = props const [editor, setEditor] = useState(null) // Keep the editor content in sync with profile.bio when it becomes available useEffect(() => { if (!editor) return const profileText = profile?.bio ? richTextToString(profile.bio as any) : '' const currentText = editor?.getText?.() ?? '' // Only update if the underlying text differs to avoid clobbering user input unnecessarily if (profileText !== currentText) { editor.commands.setContent(profile?.bio ?? '') } }, [profile?.bio, editor]) return ( { if (!editor) return onChange(editor) }} onClickTips={onClickTips} onEditor={(e) => { setEditor(e) onEditor?.(e) }} /> ) } interface BaseBioProps { defaultValue?: any onBlur?: (editor: any) => void onEditor?: (editor: any) => void onClickTips?: () => void } export function BaseBio({defaultValue, onBlur, onEditor, onClickTips}: BaseBioProps) { const t = useT() const editor = useTextEditor({ // extensions: [StarterKit], max: MAX_DESCRIPTION_LENGTH, defaultValue: defaultValue, placeholder: t( 'profile.bio.placeholder', "Tell us all the details about yourself — and what you're looking for!", ), }) const textLength = editor?.getText().length ?? 0 const remainingChars = MIN_BIO_LENGTH - textLength useEffect(() => { onEditor?.(editor) }, [editor, onEditor]) return (
{textLength < MIN_BIO_LENGTH && (

{remainingChars === 1 ? t( 'profile.bio.add_characters_one', 'Add {count} more character so you can appear in search results—or take your time and start by exploring others.', {count: remainingChars}, ) : t( 'profile.bio.add_characters_many', 'Add {count} more characters so you can appear in search results—or take your time and start by exploring others.', {count: remainingChars}, )}

)} onBlur?.(editor)} />
) } interface BaseTextEditorProps { placeholder?: any defaultValue?: any onBlur?: (editor: any) => void onEditor?: (editor: any) => void onChange?: () => void } export function BaseTextEditor({ placeholder, defaultValue, onBlur, onEditor, onChange, }: BaseTextEditorProps) { const t = useT() const editor = useTextEditor({ max: MAX_DESCRIPTION_LENGTH, defaultValue: defaultValue, placeholder: placeholder ?? t('common.text_editor.placeholder', 'Write here...'), onChange: onChange, }) useEffect(() => { onEditor?.(editor) }, [editor, onEditor]) return onBlur?.(editor)} /> }