Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b1389cdf27 | |||
| e8793e49c8 | |||
| d1666345ae |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path fill="#000" d="M18 2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7v2h15a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h3V5a1 1 0 0 1 1-1h11V3a1 1 0 0 1 1-1ZM3 20h18V10H3v10Zm3-4.25a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5Zm12 0a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5ZM14 16a1 1 0 1 1 0 2h-4a1 1 0 1 1 0-2h4Zm-8.268 1.42h0Zm-.073-.057h0Zm-.06-.067h0Zm-.047-.078h0Zm-.033-.09q.006.027.016.055-.01-.027-.016-.055ZM5.5 17h0Zm.5-5.25a1.25 1.25 0 1 1-1.243 1.378L4.75 13l.007-.128A1.25 1.25 0 0 1 6 11.75Zm4 0a1.25 1.25 0 1 1-1.243 1.378L8.75 13l.007-.128A1.25 1.25 0 0 1 10 11.75Zm4 0a1.25 1.25 0 1 1-1.243 1.378L12.75 13l.007-.128A1.25 1.25 0 0 1 14 11.75Zm4 0a1.25 1.25 0 1 1-1.243 1.378L16.75 13l.007-.128A1.25 1.25 0 0 1 18 11.75ZM5.732 13.42h0Zm-.073-.057h0Zm-.06-.067h0Zm-.047-.078h0Zm-.033-.09q.006.028.016.055-.01-.028-.016-.055ZM5.5 13h0Z"/></svg>
|
||||
|
After Width: | Height: | Size: 940 B |
@@ -0,0 +1,39 @@
|
||||
apply plugin: 'com.android.library'
|
||||
|
||||
group = 'expo.modules.keyboardlanguage'
|
||||
version = '1.0.0'
|
||||
|
||||
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
||||
apply from: expoModulesCorePlugin
|
||||
applyKotlinExpoModulesCorePlugin()
|
||||
useCoreDependencies()
|
||||
useExpoPublishing()
|
||||
|
||||
def useManagedAndroidSdkVersions = false
|
||||
if (useManagedAndroidSdkVersions) {
|
||||
useDefaultAndroidSdkVersions()
|
||||
} else {
|
||||
buildscript {
|
||||
ext.safeExtGet = { prop, fallback ->
|
||||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
|
||||
}
|
||||
}
|
||||
project.android {
|
||||
compileSdkVersion safeExtGet("compileSdkVersion", 34)
|
||||
defaultConfig {
|
||||
minSdkVersion safeExtGet("minSdkVersion", 21)
|
||||
targetSdkVersion safeExtGet("targetSdkVersion", 34)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
namespace "expo.modules.keyboardlanguage"
|
||||
defaultConfig {
|
||||
versionCode 1
|
||||
versionName "1.0.0"
|
||||
}
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
package expo.modules.keyboardlanguage
|
||||
|
||||
import android.database.ContentObserver
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.core.content.getSystemService
|
||||
import expo.modules.kotlin.modules.Module
|
||||
import expo.modules.kotlin.modules.ModuleDefinition
|
||||
|
||||
class ExpoKeyboardLanguageModule : Module() {
|
||||
private var subtypeObserver: ContentObserver? = null
|
||||
private var lastLanguage: String? = null
|
||||
|
||||
override fun definition() =
|
||||
ModuleDefinition {
|
||||
Name("ExpoKeyboardLanguage")
|
||||
|
||||
Events("onKeyboardLanguageChange")
|
||||
|
||||
Function("getCurrentKeyboardLanguage") {
|
||||
return@Function getCurrentLanguage()
|
||||
}
|
||||
|
||||
OnStartObserving {
|
||||
val context = appContext.reactContext ?: return@OnStartObserving
|
||||
val uri = Settings.Secure.getUriFor("selected_input_method_subtype")
|
||||
|
||||
subtypeObserver = object : ContentObserver(Handler(Looper.getMainLooper())) {
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
val language = getCurrentLanguage()
|
||||
if (language != lastLanguage) {
|
||||
lastLanguage = language
|
||||
sendEvent("onKeyboardLanguageChange", mapOf("language" to language))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context.contentResolver.registerContentObserver(uri, false, subtypeObserver!!)
|
||||
}
|
||||
|
||||
OnStopObserving {
|
||||
subtypeObserver?.let { observer ->
|
||||
appContext.reactContext?.contentResolver?.unregisterContentObserver(observer)
|
||||
}
|
||||
subtypeObserver = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrentLanguage(): String? {
|
||||
val context = appContext.reactContext ?: return null
|
||||
val imm = context.getSystemService<InputMethodManager>() ?: return null
|
||||
val subtype = imm.currentInputMethodSubtype ?: return null
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val tag = subtype.languageTag
|
||||
if (tag.isNotEmpty()) return tag
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
val locale = subtype.locale
|
||||
if (locale.isNotEmpty()) return locale
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"platforms": ["ios", "android"],
|
||||
"ios": {
|
||||
"modules": ["ExpoKeyboardLanguageModule"]
|
||||
},
|
||||
"android": {
|
||||
"modules": ["expo.modules.keyboardlanguage.ExpoKeyboardLanguageModule"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export {
|
||||
addKeyboardLanguageListener,
|
||||
getCurrentKeyboardLanguage,
|
||||
} from './src/index'
|
||||
@@ -0,0 +1,20 @@
|
||||
Pod::Spec.new do |s|
|
||||
s.name = 'ExpoKeyboardLanguage'
|
||||
s.version = '1.0.0'
|
||||
s.summary = 'Expo module to detect the current keyboard language'
|
||||
s.description = 'Expo module to detect the current keyboard language'
|
||||
s.author = ''
|
||||
s.homepage = 'https://github.com/bluesky-social/social-app'
|
||||
s.platforms = { :ios => '13.4' }
|
||||
s.source = { git: '' }
|
||||
s.static_framework = true
|
||||
|
||||
s.dependency 'ExpoModulesCore'
|
||||
|
||||
s.pod_target_xcconfig = {
|
||||
'DEFINES_MODULE' => 'YES',
|
||||
'SWIFT_COMPILATION_MODE' => 'wholemodule'
|
||||
}
|
||||
|
||||
s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
|
||||
end
|
||||
@@ -0,0 +1,75 @@
|
||||
import ExpoModulesCore
|
||||
|
||||
public class ExpoKeyboardLanguageModule: Module {
|
||||
public func definition() -> ModuleDefinition {
|
||||
Name("ExpoKeyboardLanguage")
|
||||
|
||||
Events("onKeyboardLanguageChange")
|
||||
|
||||
Function("getCurrentKeyboardLanguage") {
|
||||
return self.currentKeyboardLanguage()
|
||||
}
|
||||
|
||||
OnStartObserving {
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(self.onInputModeOrResponderChange),
|
||||
name: UITextInputMode.currentInputModeDidChangeNotification,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.addObserver(
|
||||
self,
|
||||
selector: #selector(self.onInputModeOrResponderChange),
|
||||
name: UIResponder.keyboardDidShowNotification,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
|
||||
OnStopObserving {
|
||||
NotificationCenter.default.removeObserver(
|
||||
self,
|
||||
name: UITextInputMode.currentInputModeDidChangeNotification,
|
||||
object: nil
|
||||
)
|
||||
NotificationCenter.default.removeObserver(
|
||||
self,
|
||||
name: UIResponder.keyboardDidShowNotification,
|
||||
object: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private var lastLanguage: String?
|
||||
|
||||
@objc
|
||||
private func onInputModeOrResponderChange() {
|
||||
let language = currentKeyboardLanguage()
|
||||
if language != lastLanguage {
|
||||
lastLanguage = language
|
||||
sendEvent("onKeyboardLanguageChange", [
|
||||
"language": language as Any
|
||||
])
|
||||
}
|
||||
}
|
||||
|
||||
private func currentKeyboardLanguage() -> String? {
|
||||
let keyWindow = UIApplication.shared.connectedScenes
|
||||
.compactMap { $0 as? UIWindowScene }
|
||||
.flatMap { $0.windows }
|
||||
.first { $0.isKeyWindow }
|
||||
|
||||
return keyWindow?.findFirstResponder()?.textInputMode?.primaryLanguage
|
||||
}
|
||||
}
|
||||
|
||||
private extension UIView {
|
||||
func findFirstResponder() -> UIView? {
|
||||
if isFirstResponder { return self }
|
||||
for subview in subviews {
|
||||
if let found = subview.findFirstResponder() {
|
||||
return found
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import {type EventSubscription} from 'expo-modules-core'
|
||||
import {requireNativeModule} from 'expo-modules-core'
|
||||
|
||||
type ExpoKeyboardLanguageModule = {
|
||||
getCurrentKeyboardLanguage(): string | null
|
||||
addListener(
|
||||
eventName: 'onKeyboardLanguageChange',
|
||||
listener: (event: {language: string | null}) => void,
|
||||
): EventSubscription
|
||||
}
|
||||
|
||||
const NativeModule = requireNativeModule<ExpoKeyboardLanguageModule>(
|
||||
'ExpoKeyboardLanguage',
|
||||
)
|
||||
|
||||
export function getCurrentKeyboardLanguage(): string | null {
|
||||
return NativeModule.getCurrentKeyboardLanguage()
|
||||
}
|
||||
|
||||
export function addKeyboardLanguageListener(
|
||||
cb: (language: string | null) => void,
|
||||
): EventSubscription {
|
||||
return NativeModule.addListener('onKeyboardLanguageChange', event => {
|
||||
cb(event.language)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import {type EventSubscription} from 'expo-modules-core'
|
||||
|
||||
export function getCurrentKeyboardLanguage(): string | null {
|
||||
return null
|
||||
}
|
||||
|
||||
export function addKeyboardLanguageListener(
|
||||
_cb: (language: string | null) => void,
|
||||
): EventSubscription {
|
||||
return {remove() {}}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import {createSinglePathSVG} from './TEMPLATE'
|
||||
|
||||
export const Keyboard_Stroke2_Corner0_Rounded = createSinglePathSVG({
|
||||
path: 'M18 2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H7v2h15a1 1 0 0 1 1 1v12a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1h3V5a1 1 0 0 1 1-1h11V3a1 1 0 0 1 1-1ZM3 20h18V10H3v10Zm3-4.25a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5Zm12 0a1.25 1.25 0 1 1 0 2.5 1.25 1.25 0 0 1 0-2.5ZM14 16a1 1 0 1 1 0 2h-4a1 1 0 1 1 0-2h4Zm-8.268 1.42h0Zm-.073-.057h0Zm-.06-.067h0Zm-.047-.078h0Zm-.033-.09q.006.027.016.055-.01-.027-.016-.055ZM5.5 17h0Zm.5-5.25a1.25 1.25 0 1 1-1.243 1.378L4.75 13l.007-.128A1.25 1.25 0 0 1 6 11.75Zm4 0a1.25 1.25 0 1 1-1.243 1.378L8.75 13l.007-.128A1.25 1.25 0 0 1 10 11.75Zm4 0a1.25 1.25 0 1 1-1.243 1.378L12.75 13l.007-.128A1.25 1.25 0 0 1 14 11.75Zm4 0a1.25 1.25 0 1 1-1.243 1.378L16.75 13l.007-.128A1.25 1.25 0 0 1 18 11.75ZM5.732 13.42h0Zm-.073-.057h0Zm-.06-.067h0Zm-.047-.078h0Zm-.033-.09q.006.028.016.055-.01-.028-.016-.055ZM5.5 13h0Z',
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
import {useEffect, useState} from 'react'
|
||||
|
||||
import {
|
||||
addKeyboardLanguageListener,
|
||||
getCurrentKeyboardLanguage,
|
||||
} from '../../../modules/expo-keyboard-language'
|
||||
|
||||
function toBcp2(tag: string | null): string | null {
|
||||
if (!tag) return null
|
||||
return tag.split('-')[0].toLowerCase()
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current keyboard language as a 2-letter code (e.g. "en"),
|
||||
* updating in real-time on iOS.
|
||||
*/
|
||||
export function useKeyboardLanguage(): string | null {
|
||||
const [language, setLanguage] = useState(() =>
|
||||
toBcp2(getCurrentKeyboardLanguage()),
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const sub = addKeyboardLanguageListener(lang => {
|
||||
setLanguage(toBcp2(lang))
|
||||
})
|
||||
return () => sub.remove()
|
||||
}, [])
|
||||
|
||||
return language
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Web stub — keyboard language detection is not available on web.
|
||||
*/
|
||||
export function useKeyboardLanguage(): null {
|
||||
return null
|
||||
}
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
import {sanitizeAppLanguageSetting} from '#/locale/helpers'
|
||||
import {APP_LANGUAGES, LANGUAGES} from '#/locale/languages'
|
||||
import {useLanguagePrefs, useLanguagePrefsApi} from '#/state/preferences'
|
||||
import {atoms as a, web} from '#/alf'
|
||||
import {atoms as a, useTheme, web} from '#/alf'
|
||||
import {Admonition} from '#/components/Admonition'
|
||||
import {Button} from '#/components/Button'
|
||||
import {useDialogControl} from '#/components/Dialog'
|
||||
@@ -21,6 +21,8 @@ import {PlusLarge_Stroke2_Corner0_Rounded as PlusIcon} from '#/components/icons/
|
||||
import * as Layout from '#/components/Layout'
|
||||
import * as Select from '#/components/Select'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {IS_NATIVE} from '#/env'
|
||||
import {device, useStorage} from '#/storage'
|
||||
import * as SettingsList from './components/SettingsList'
|
||||
|
||||
const DEDUPED_LANGUAGES = LANGUAGES.filter(
|
||||
@@ -31,8 +33,12 @@ const DEDUPED_LANGUAGES = LANGUAGES.filter(
|
||||
type Props = NativeStackScreenProps<CommonNavigatorParams, 'LanguageSettings'>
|
||||
export function LanguageSettingsScreen({}: Props) {
|
||||
const {_} = useLingui()
|
||||
const t = useTheme()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
const setLangPrefs = useLanguagePrefsApi()
|
||||
const [useKbLang = false, setUseKbLang] = useStorage(device, [
|
||||
'useKeyboardLanguage',
|
||||
])
|
||||
|
||||
const contentLanguagePrefsControl = useDialogControl()
|
||||
|
||||
@@ -234,6 +240,43 @@ export function LanguageSettingsScreen({}: Props) {
|
||||
/>
|
||||
</View>
|
||||
</SettingsList.Group>
|
||||
{IS_NATIVE && (
|
||||
<>
|
||||
<SettingsList.Divider />
|
||||
<SettingsList.Group iconInset={false}>
|
||||
<SettingsList.ItemText>
|
||||
<Trans>Post language</Trans>
|
||||
</SettingsList.ItemText>
|
||||
<Toggle.Item
|
||||
name="use_keyboard_language"
|
||||
label={_(
|
||||
msg`Automatically detect post language from keyboard`,
|
||||
)}
|
||||
value={useKbLang}
|
||||
onChange={value => setUseKbLang(value)}
|
||||
style={[a.w_full, a.gap_md]}>
|
||||
<Text style={[a.flex_1]}>
|
||||
<Trans>
|
||||
Automatically detect post language from keyboard
|
||||
</Trans>
|
||||
</Text>
|
||||
<Toggle.Platform />
|
||||
</Toggle.Item>
|
||||
<Text
|
||||
style={[
|
||||
a.text_xs,
|
||||
a.leading_snug,
|
||||
t.atoms.text_contrast_medium,
|
||||
a.mt_sm,
|
||||
]}>
|
||||
<Trans>
|
||||
Experimental. When enabled, the composer will default to
|
||||
your keyboard language.
|
||||
</Trans>
|
||||
</Text>
|
||||
</SettingsList.Group>
|
||||
</>
|
||||
)}
|
||||
</SettingsList.Container>
|
||||
</Layout.Content>
|
||||
</Layout.Screen>
|
||||
|
||||
@@ -12,7 +12,7 @@ type ApiContext = {
|
||||
setPrimaryLanguage: (code2: string) => void
|
||||
setPostLanguage: (commaSeparatedLangCodes: string) => void
|
||||
setContentLanguages: (code2s: string[]) => void
|
||||
savePostLanguageToHistory: () => void
|
||||
savePostLanguageToHistory: (resolvedLanguage?: string) => void
|
||||
setAppLanguage: (code2: AppLanguage) => void
|
||||
}
|
||||
|
||||
@@ -61,21 +61,28 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
/**
|
||||
* Saves whatever language codes are currently selected into a history array,
|
||||
* which is then used to populate the language selector menu.
|
||||
*
|
||||
* If `resolvedLanguage` is provided (e.g. from auto-detected keyboard
|
||||
* language), it will be saved to history instead of the current
|
||||
* `postLanguage` preference.
|
||||
*/
|
||||
savePostLanguageToHistory() {
|
||||
savePostLanguageToHistory(resolvedLanguage?: string) {
|
||||
// filter out duplicate `this.postLanguage` if exists, and prepend
|
||||
// value to start of array
|
||||
setStateWrapped(s => ({
|
||||
...s,
|
||||
postLanguageHistory: [s.postLanguage]
|
||||
.concat(
|
||||
s.postLanguageHistory.filter(
|
||||
commaSeparatedLangCodes =>
|
||||
commaSeparatedLangCodes !== s.postLanguage,
|
||||
),
|
||||
)
|
||||
.slice(0, 6),
|
||||
}))
|
||||
setStateWrapped(s => {
|
||||
const langToSave = resolvedLanguage ?? s.postLanguage
|
||||
return {
|
||||
...s,
|
||||
postLanguageHistory: [langToSave]
|
||||
.concat(
|
||||
s.postLanguageHistory.filter(
|
||||
commaSeparatedLangCodes =>
|
||||
commaSeparatedLangCodes !== langToSave,
|
||||
),
|
||||
)
|
||||
.slice(0, 6),
|
||||
}
|
||||
})
|
||||
},
|
||||
setAppLanguage(code2: AppLanguage) {
|
||||
setStateWrapped(s => ({...s, appLanguage: code2}))
|
||||
|
||||
@@ -61,6 +61,12 @@ export type Device = {
|
||||
activitySubscriptionsNudged?: boolean
|
||||
threadgateNudged?: boolean
|
||||
|
||||
/**
|
||||
* Whether to automatically detect post language from the keyboard language.
|
||||
* Experimental feature, opt-in.
|
||||
*/
|
||||
useKeyboardLanguage?: boolean
|
||||
|
||||
/**
|
||||
* Policy update overlays. New IDs are required for each new announcement.
|
||||
*/
|
||||
|
||||
@@ -70,6 +70,7 @@ import {
|
||||
type SupportedMimeTypes,
|
||||
} from '#/lib/constants'
|
||||
import {useIsKeyboardVisible} from '#/lib/hooks/useIsKeyboardVisible'
|
||||
import {useKeyboardLanguage} from '#/lib/hooks/useKeyboardLanguage'
|
||||
import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback'
|
||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||
import {mimeToExt} from '#/lib/media/video/util'
|
||||
@@ -134,6 +135,7 @@ import * as Toast from '#/components/Toast'
|
||||
import {Text} from '#/components/Typography'
|
||||
import {useAnalytics} from '#/analytics'
|
||||
import {IS_ANDROID, IS_IOS, IS_LIQUID_GLASS, IS_NATIVE, IS_WEB} from '#/env'
|
||||
import {device, useStorage} from '#/storage'
|
||||
import {BottomSheetPortalProvider} from '../../../../modules/bottom-sheet'
|
||||
import {
|
||||
draftToComposerPosts,
|
||||
@@ -243,26 +245,52 @@ export const ComposePost = ({
|
||||
replyTo?.langs || [],
|
||||
)
|
||||
|
||||
/**
|
||||
* Automatic keyboard language detection.
|
||||
*/
|
||||
const keyboardLanguage = useKeyboardLanguage()
|
||||
const [autoLangEnabled = false] = useStorage(device, ['useKeyboardLanguage'])
|
||||
const [manualOverride, setManualOverride] = useState(false)
|
||||
const isAutoMode = !!autoLangEnabled && !manualOverride
|
||||
|
||||
/**
|
||||
* The currently selected languages of the post. Prefer local temporary
|
||||
* language suggestion over global lang prefs, if available.
|
||||
* language suggestion, then auto-detected keyboard language, then global
|
||||
* lang prefs.
|
||||
*/
|
||||
const currentLanguages = useMemo(
|
||||
() =>
|
||||
acceptedLanguageSuggestion
|
||||
? [acceptedLanguageSuggestion]
|
||||
: toPostLanguages(langPrefs.postLanguage),
|
||||
[acceptedLanguageSuggestion, langPrefs.postLanguage],
|
||||
: isAutoMode && keyboardLanguage
|
||||
? [keyboardLanguage]
|
||||
: toPostLanguages(langPrefs.postLanguage),
|
||||
[
|
||||
acceptedLanguageSuggestion,
|
||||
isAutoMode,
|
||||
keyboardLanguage,
|
||||
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.
|
||||
* previously, and any we might try to suggest to them. Also locks out
|
||||
* auto mode for this session.
|
||||
*/
|
||||
const onSelectLanguage = () => {
|
||||
setAcceptedLanguageSuggestion(null)
|
||||
setReplyToLanguages([])
|
||||
setManualOverride(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-enable auto mode when the user taps "Automatic" in the language menu.
|
||||
*/
|
||||
const onSelectAutomatic = () => {
|
||||
setAcceptedLanguageSuggestion(null)
|
||||
setManualOverride(false)
|
||||
}
|
||||
|
||||
const [composerState, composerDispatch] = useReducer(
|
||||
@@ -939,7 +967,7 @@ export const ComposePost = ({
|
||||
originalLocalRefs: composerState.originalLocalRefs,
|
||||
})
|
||||
}
|
||||
setLangPrefs.savePostLanguageToHistory()
|
||||
setLangPrefs.savePostLanguageToHistory(fromPostLanguages(currentLanguages))
|
||||
if (initQuote) {
|
||||
// We want to wait for the quote count to update before we call `onPost`, which will refetch data
|
||||
whenAppViewReady(agent, initQuote.uri, res => {
|
||||
@@ -1122,6 +1150,10 @@ export const ComposePost = ({
|
||||
}}
|
||||
currentLanguages={currentLanguages}
|
||||
onSelectLanguage={onSelectLanguage}
|
||||
onSelectAutomatic={onSelectAutomatic}
|
||||
keyboardLanguage={keyboardLanguage}
|
||||
isAutoMode={isAutoMode}
|
||||
autoLangEnabled={autoLangEnabled}
|
||||
openGallery={openGallery}
|
||||
/>
|
||||
</>
|
||||
@@ -1823,6 +1855,10 @@ function ComposerFooter({
|
||||
onAddPost,
|
||||
currentLanguages,
|
||||
onSelectLanguage,
|
||||
onSelectAutomatic,
|
||||
keyboardLanguage,
|
||||
isAutoMode,
|
||||
autoLangEnabled,
|
||||
openGallery,
|
||||
}: {
|
||||
post: PostDraft
|
||||
@@ -1834,6 +1870,10 @@ function ComposerFooter({
|
||||
onAddPost: () => void
|
||||
currentLanguages: string[]
|
||||
onSelectLanguage?: (language: string) => void
|
||||
onSelectAutomatic?: () => void
|
||||
keyboardLanguage: string | null
|
||||
isAutoMode: boolean
|
||||
autoLangEnabled: boolean
|
||||
openGallery?: boolean
|
||||
}) {
|
||||
const t = useTheme()
|
||||
@@ -1992,6 +2032,10 @@ function ComposerFooter({
|
||||
<PostLanguageSelect
|
||||
currentLanguages={currentLanguages}
|
||||
onSelectLanguage={onSelectLanguage}
|
||||
onSelectAutomatic={onSelectAutomatic}
|
||||
keyboardLanguage={keyboardLanguage}
|
||||
isAutoMode={isAutoMode}
|
||||
autoLangEnabled={autoLangEnabled}
|
||||
/>
|
||||
<CharProgress
|
||||
count={post.shortenedGraphemeLength}
|
||||
|
||||
@@ -15,15 +15,24 @@ import * as Dialog from '#/components/Dialog'
|
||||
import {LanguageSelectDialog} from '#/components/dialogs/LanguageSelectDialog'
|
||||
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRightIcon} from '#/components/icons/Chevron'
|
||||
import {Globe_Stroke2_Corner0_Rounded as GlobeIcon} from '#/components/icons/Globe'
|
||||
import {Keyboard_Stroke2_Corner0_Rounded as KeyboardIcon} from '#/components/icons/Keyboard'
|
||||
import * as Menu from '#/components/Menu'
|
||||
import {Text} from '#/components/Typography'
|
||||
|
||||
export function PostLanguageSelect({
|
||||
currentLanguages: currentLanguagesProp,
|
||||
onSelectLanguage,
|
||||
onSelectAutomatic,
|
||||
keyboardLanguage,
|
||||
isAutoMode,
|
||||
autoLangEnabled,
|
||||
}: {
|
||||
currentLanguages?: string[]
|
||||
onSelectLanguage?: (language: string) => void
|
||||
onSelectAutomatic?: () => void
|
||||
keyboardLanguage?: string | null
|
||||
isAutoMode?: boolean
|
||||
autoLangEnabled?: boolean
|
||||
}) {
|
||||
const {_} = useLingui()
|
||||
const langPrefs = useLanguagePrefs()
|
||||
@@ -46,26 +55,7 @@ export function PostLanguageSelect({
|
||||
onSelectLanguage?.(langsString)
|
||||
}
|
||||
|
||||
if (
|
||||
dedupedHistory.length === 1 &&
|
||||
dedupedHistory[0] === langPrefs.postLanguage
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<LanguageBtn onPress={languageDialogControl.open} />
|
||||
<LanguageSelectDialog
|
||||
titleText={<Trans>Choose post languages</Trans>}
|
||||
subtitleText={
|
||||
<Trans>Select up to 3 languages used in this post</Trans>
|
||||
}
|
||||
control={languageDialogControl}
|
||||
currentLanguages={currentLanguages}
|
||||
onSelectLanguages={onSelectLanguages}
|
||||
maxLanguages={3}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
const showAutoItem = !!autoLangEnabled && !!keyboardLanguage
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -76,6 +66,31 @@ export function PostLanguageSelect({
|
||||
)}
|
||||
</Menu.Trigger>
|
||||
<Menu.Outer>
|
||||
{showAutoItem && (
|
||||
<Menu.Group>
|
||||
<Menu.Item
|
||||
label={_(
|
||||
msg`Keyboard (${codeToLanguageName(
|
||||
keyboardLanguage,
|
||||
langPrefs.appLanguage,
|
||||
)})`,
|
||||
)}
|
||||
onPress={() => onSelectAutomatic?.()}>
|
||||
<Menu.ItemIcon icon={KeyboardIcon} />
|
||||
<Menu.ItemText>
|
||||
<Trans>
|
||||
Keyboard (
|
||||
{codeToLanguageName(
|
||||
keyboardLanguage,
|
||||
langPrefs.appLanguage,
|
||||
)}
|
||||
)
|
||||
</Trans>
|
||||
</Menu.ItemText>
|
||||
<Menu.ItemRadio selected={!!isAutoMode} />
|
||||
</Menu.Item>
|
||||
</Menu.Group>
|
||||
)}
|
||||
<Menu.Group>
|
||||
{dedupedHistory.map(historyItem => {
|
||||
const langCodes = historyItem.split(',')
|
||||
@@ -92,7 +107,9 @@ export function PostLanguageSelect({
|
||||
}}>
|
||||
<Menu.ItemText>{langName}</Menu.ItemText>
|
||||
<Menu.ItemRadio
|
||||
selected={currentLanguages.includes(historyItem)}
|
||||
selected={
|
||||
!isAutoMode && currentLanguages.includes(historyItem)
|
||||
}
|
||||
/>
|
||||
</Menu.Item>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user