Skip to content
Merged
2 changes: 1 addition & 1 deletion src/state/persisted/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ const schema = z.object({
contentLanguages: z.array(z.string()),
/**
* The language(s) the user is currently posting in, configured within the
* composer. Multiple languages are psearate by commas.
* composer. Multiple languages are separated by commas.
*
* BCP-47 2-letter language code without region.
*/
Expand Down
4 changes: 4 additions & 0 deletions src/state/preferences/languages.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,10 @@ export function toPostLanguages(postLanguage: string): string[] {
return postLanguage.split(',').filter(Boolean)
}

export function fromPostLanguages(languages: string[]): string {
return languages.filter(Boolean).join(',')
}

export function hasPostLanguage(postLanguage: string, code2: string): boolean {
return toPostLanguages(postLanguage).includes(code2)
}
62 changes: 56 additions & 6 deletions src/view/com/composer/Composer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ import {
import {useModalControls} from '#/state/modals'
import {useRequireAltTextEnabled} from '#/state/preferences'
import {
fromPostLanguages,
toPostLanguages,
useLanguagePrefs,
useLanguagePrefsApi,
Expand Down Expand Up @@ -197,6 +198,45 @@ export const ComposePost = ({
const [publishingStage, setPublishingStage] = useState('')
const [error, setError] = useState('')

/**
* A temporarly local reference to a language suggestion that the user has
* accepted. This overrides the global post language preference, but is not
* stored permanently.
*/
const [acceptedLanguageSuggestion, setAcceptedLanguageSuggestion] = useState<
string | null
>(null)

/**
* The language of the post being replied to, if any. We just use the first
* language available, for now.
*/
const [replyToLanguage, setReplyToLanguage] = useState<string | undefined>(
replyTo?.langs?.[0],
)

/**
* The currently selected languages of the post. Prefer local temporary
* language suggestion over global lang prefs, if available.
*/
const currentLanguages = useMemo(
() =>
acceptedLanguageSuggestion
? [acceptedLanguageSuggestion]
: toPostLanguages(langPrefs.postLanguage),
[acceptedLanguageSuggestion, langPrefs.postLanguage],
)

/**
* When the user selects a language from the composer language selector,
* clear any temporary language suggestions they may have selected
* previously, and any we might try to suggest to them.
*/
const onSelectLanguage = () => {
setAcceptedLanguageSuggestion(null)
setReplyToLanguage(undefined)
}

const [composerState, composerDispatch] = useReducer(
composerReducer,
{
Expand Down Expand Up @@ -414,7 +454,7 @@ export const ComposePost = ({
thread,
replyTo: replyTo?.uri,
onStateChange: setPublishingStage,
langs: toPostLanguages(langPrefs.postLanguage),
langs: currentLanguages,
})
).uris[0]

Expand Down Expand Up @@ -490,7 +530,7 @@ export const ComposePost = ({
isPartOfThread: thread.posts.length > 1,
hasLink: !!post.embed.link,
hasQuote: !!post.embed.quote,
langs: langPrefs.postLanguage,
langs: fromPostLanguages(currentLanguages),
logContext: 'Composer',
})
index++
Expand Down Expand Up @@ -557,7 +597,7 @@ export const ComposePost = ({
thread,
canPost,
isPublishing,
langPrefs.postLanguage,
currentLanguages,
onClose,
onPost,
onPostSuccess,
Expand Down Expand Up @@ -654,8 +694,9 @@ export const ComposePost = ({
<>
<SuggestedLanguage
text={activePost.richtext.text}
// NOTE(@elijaharita): currently just choosing the first language if any exists
replyToLanguage={replyTo?.langs?.[0]}
replyToLanguage={replyToLanguage}
currentLanguages={currentLanguages}
onAcceptSuggestedLanguage={setAcceptedLanguageSuggestion}
/>
<ComposerPills
isReply={!!replyTo}
Expand All @@ -678,6 +719,8 @@ export const ComposePost = ({
type: 'add_post',
})
}}
currentLanguages={currentLanguages}
onSelectLanguage={onSelectLanguage}
/>
</>
)
Expand Down Expand Up @@ -1289,6 +1332,8 @@ function ComposerFooter({
onEmojiButtonPress,
onSelectVideo,
onAddPost,
currentLanguages,
onSelectLanguage,
}: {
post: PostDraft
dispatch: (action: PostAction) => void
Expand All @@ -1297,6 +1342,8 @@ function ComposerFooter({
onError: (error: string) => void
onSelectVideo: (postId: string, asset: ImagePickerAsset) => void
onAddPost: () => void
currentLanguages: string[]
onSelectLanguage?: (language: string) => void
}) {
const t = useTheme()
const {_} = useLingui()
Expand Down Expand Up @@ -1450,7 +1497,10 @@ function ComposerFooter({
<PlusIcon size="lg" />
</Button>
)}
<PostLanguageSelect />
<PostLanguageSelect
currentLanguages={currentLanguages}
onSelectLanguage={onSelectLanguage}
/>
<CharProgress
count={post.shortenedGraphemeLength}
style={{width: 65}}
Expand Down
44 changes: 35 additions & 9 deletions src/view/com/composer/select-language/PostLanguageSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,13 @@ import * as Menu from '#/components/Menu'
import {Text} from '#/components/Typography'
import {PostLanguageSelectDialog} from './PostLanguageSelectDialog'

export function PostLanguageSelect() {
export function PostLanguageSelect({
currentLanguages: currentLanguagesProp,
onSelectLanguage,
}: {
currentLanguages?: string[]
onSelectLanguage?: (language: string) => void
}) {
const {_} = useLingui()
const langPrefs = useLanguagePrefs()
const setLangPrefs = useLanguagePrefsApi()
Expand All @@ -27,14 +33,20 @@ export function PostLanguageSelect() {
new Set([...langPrefs.postLanguageHistory, langPrefs.postLanguage]),
)

const currentLanguages =
currentLanguagesProp ?? toPostLanguages(langPrefs.postLanguage)

if (
dedupedHistory.length === 1 &&
dedupedHistory[0] === langPrefs.postLanguage
) {
return (
<>
<LanguageBtn onPress={languageDialogControl.open} />
<PostLanguageSelectDialog control={languageDialogControl} />
<PostLanguageSelectDialog
control={languageDialogControl}
currentLanguages={currentLanguages}
/>
</>
)
}
Expand All @@ -43,7 +55,9 @@ export function PostLanguageSelect() {
<>
<Menu.Root>
<Menu.Trigger label={_(msg`Select post language`)}>
{({props}) => <LanguageBtn {...props} />}
{({props}) => (
<LanguageBtn currentLanguages={currentLanguages} {...props} />
)}
</Menu.Trigger>
<Menu.Outer>
<Menu.Group>
Expand All @@ -56,10 +70,13 @@ export function PostLanguageSelect() {
<Menu.Item
key={historyItem}
label={_(msg`Select ${langName}`)}
onPress={() => setLangPrefs.setPostLanguage(historyItem)}>
onPress={() => {
setLangPrefs.setPostLanguage(historyItem)
onSelectLanguage?.(historyItem)
}}>
<Menu.ItemText>{langName}</Menu.ItemText>
<Menu.ItemRadio
selected={historyItem === langPrefs.postLanguage}
selected={currentLanguages.includes(historyItem)}
/>
</Menu.Item>
)
Expand All @@ -77,17 +94,26 @@ export function PostLanguageSelect() {
</Menu.Outer>
</Menu.Root>

<PostLanguageSelectDialog control={languageDialogControl} />
<PostLanguageSelectDialog
control={languageDialogControl}
currentLanguages={currentLanguages}
onSelectLanguage={onSelectLanguage}
/>
</>
)
}

function LanguageBtn(props: Omit<ButtonProps, 'label' | 'children'>) {
function LanguageBtn(
props: Omit<ButtonProps, 'label' | 'children'> & {
currentLanguages?: string[]
},
) {
const {_} = useLingui()
const langPrefs = useLanguagePrefs()
const t = useTheme()

const postLanguagesPref = toPostLanguages(langPrefs.postLanguage)
const currentLanguages = props.currentLanguages ?? postLanguagesPref

return (
<Button
Expand All @@ -106,7 +132,7 @@ function LanguageBtn(props: Omit<ButtonProps, 'label' | 'children'>) {
{({pressed, hovered}) => {
const color =
pressed || hovered ? t.palette.primary_300 : t.palette.primary_500
if (postLanguagesPref.length > 0) {
if (currentLanguages.length > 0) {
return (
<Text
style={[
Expand All @@ -117,7 +143,7 @@ function LanguageBtn(props: Omit<ButtonProps, 'label' | 'children'>) {
{maxWidth: 100},
]}
numberOfLines={1}>
{postLanguagesPref
{currentLanguages
.map(lang => codeToLanguageName(lang, langPrefs.appLanguage))
.join(', ')}
</Text>
Expand Down
28 changes: 25 additions & 3 deletions src/view/com/composer/select-language/PostLanguageSelectDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {languageName} from '#/locale/helpers'
import {type Language, LANGUAGES, LANGUAGES_MAP_CODE2} from '#/locale/languages'
import {isNative, isWeb} from '#/platform/detection'
import {
toPostLanguages,
useLanguagePrefs,
useLanguagePrefsApi,
} from '#/state/preferences/languages'
Expand All @@ -23,8 +24,16 @@ import {Text} from '#/components/Typography'

export function PostLanguageSelectDialog({
control,
/**
* Optionally can be passed to show different values than what is saved in
* langPrefs.
*/
currentLanguages,
onSelectLanguage,
}: {
control: Dialog.DialogControlProps
currentLanguages?: string[]
onSelectLanguage?: (language: string) => void
}) {
const {height} = useWindowDimensions()
const insets = useSafeAreaInsets()
Expand All @@ -40,13 +49,22 @@ export function PostLanguageSelectDialog({
nativeOptions={{minHeight: height - insets.top}}>
<Dialog.Handle />
<ErrorBoundary renderError={renderErrorBoundary}>
<DialogInner />
<DialogInner
currentLanguages={currentLanguages}
onSelectLanguage={onSelectLanguage}
/>
</ErrorBoundary>
</Dialog.Outer>
)
}

export function DialogInner() {
export function DialogInner({
currentLanguages,
onSelectLanguage,
}: {
currentLanguages?: string[]
onSelectLanguage?: (language: string) => void
}) {
const control = Dialog.useDialogContext()
const [headerHeight, setHeaderHeight] = useState(0)

Expand All @@ -63,8 +81,11 @@ export function DialogInner() {
}, [])

const langPrefs = useLanguagePrefs()
const postLanguagesPref =
currentLanguages ?? toPostLanguages(langPrefs.postLanguage)

const [checkedLanguagesCode2, setCheckedLanguagesCode2] = useState<string[]>(
langPrefs.postLanguage.split(',') || [langPrefs.primaryLanguage],
postLanguagesPref || [langPrefs.primaryLanguage],
)
const [search, setSearch] = useState('')

Expand All @@ -79,6 +100,7 @@ export function DialogInner() {
langsString = langPrefs.primaryLanguage
}
setLangPrefs.setPostLanguage(langsString)
onSelectLanguage?.(langsString)
})
}

Expand Down
Loading
Loading