mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

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

Native translation expo module (#4098)

* translation expo module

* add `onClose` and `onReplacementAction`

* rm onReplacementAction

* make all props published

* make translation api available globally w/o wrapper (#4110)

* conditionally import the translation module

* only use native translation if language is probably supported

* open native translation via dropdown menu

---------

Co-authored-by: Hailey <me@haileyok.com>
Co-authored-by: Dan Abramov <dan.abramov@gmail.com>

authored by samuel.fm

Hailey
Dan Abramov
and committed by
GitHub
b59c8e22 a60f9933

+232 -8
+6
modules/expo-bluesky-translate/expo-module.config.json
··· 1 + { 2 + "platforms": ["ios"], 3 + "ios": { 4 + "modules": ["ExpoBlueskyTranslateModule"] 5 + } 6 + }
+6
modules/expo-bluesky-translate/index.ts
··· 1 + export { 2 + isAvailable, 3 + isLanguageSupported, 4 + NativeTranslationModule, 5 + NativeTranslationView, 6 + } from './src/ExpoBlueskyTranslateView'
+20
modules/expo-bluesky-translate/ios/Common/UIHostingControllerCompat.swift
··· 1 + import ExpoModulesCore 2 + import SwiftUI 3 + 4 + // Thanks to Andrew Levy for this code snippet 5 + // https://github.com/andrew-levy/swiftui-react-native/blob/d3fbb2abf07601ff0d4b83055e7717bb980910d6/ios/Common/ExpoView%2BUIHostingController.swift 6 + 7 + extension ExpoView { 8 + func setupHostingController(_ hostingController: UIHostingController<some View>) { 9 + hostingController.view.translatesAutoresizingMaskIntoConstraints = false 10 + hostingController.view.backgroundColor = .clear 11 + 12 + addSubview(hostingController.view) 13 + NSLayoutConstraint.activate([ 14 + hostingController.view.topAnchor.constraint(equalTo: self.topAnchor), 15 + hostingController.view.bottomAnchor.constraint(equalTo: self.bottomAnchor), 16 + hostingController.view.leftAnchor.constraint(equalTo: self.leftAnchor), 17 + hostingController.view.rightAnchor.constraint(equalTo: self.rightAnchor), 18 + ]) 19 + } 20 + }
+21
modules/expo-bluesky-translate/ios/ExpoBlueskyTranslate.podspec
··· 1 + Pod::Spec.new do |s| 2 + s.name = 'ExpoBlueskyTranslate' 3 + s.version = '1.0.0' 4 + s.summary = 'Uses SwiftUI translation to translate text.' 5 + s.description = 'Uses SwiftUI translation to translate text.' 6 + s.author = '' 7 + s.homepage = 'https://docs.expo.dev/modules/' 8 + s.platforms = { :ios => '13.4' } 9 + s.source = { git: '' } 10 + s.static_framework = true 11 + 12 + s.dependency 'ExpoModulesCore' 13 + 14 + # Swift/Objective-C compatibility 15 + s.pod_target_xcconfig = { 16 + 'DEFINES_MODULE' => 'YES', 17 + 'SWIFT_COMPILATION_MODE' => 'wholemodule' 18 + } 19 + 20 + s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}" 21 + end
+18
modules/expo-bluesky-translate/ios/ExpoBlueskyTranslateModule.swift
··· 1 + import ExpoModulesCore 2 + import Foundation 3 + import SwiftUI 4 + 5 + public class ExpoBlueskyTranslateModule: Module { 6 + public func definition() -> ModuleDefinition { 7 + Name("ExpoBlueskyTranslate") 8 + 9 + AsyncFunction("presentAsync") { (text: String) in 10 + DispatchQueue.main.async { [weak state = TranslateViewState.shared] in 11 + state?.isPresented = true 12 + state?.text = text 13 + } 14 + } 15 + 16 + View(ExpoBlueskyTranslateView.self) {} 17 + } 18 + }
+22
modules/expo-bluesky-translate/ios/ExpoBlueskyTranslateView.swift
··· 1 + import ExpoModulesCore 2 + import Foundation 3 + import SwiftUI 4 + 5 + class TranslateViewState: ObservableObject { 6 + static var shared = TranslateViewState() 7 + 8 + @Published var isPresented = false 9 + @Published var text = "" 10 + } 11 + 12 + class ExpoBlueskyTranslateView: ExpoView { 13 + required init(appContext: AppContext? = nil) { 14 + if #available(iOS 14.0, *) { 15 + let hostingController = UIHostingController(rootView: TranslateView()) 16 + super.init(appContext: appContext) 17 + setupHostingController(hostingController) 18 + } else { 19 + super.init(appContext: appContext) 20 + } 21 + } 22 + }
+31
modules/expo-bluesky-translate/ios/TranslateView.swift
··· 1 + import SwiftUI 2 + // conditionally import the Translation module 3 + #if canImport(Translation) 4 + import Translation 5 + #endif 6 + 7 + struct TranslateView: View { 8 + @ObservedObject var state = TranslateViewState.shared 9 + 10 + var body: some View { 11 + if #available(iOS 17.4, *) { 12 + VStack { 13 + UIViewRepresentableWrapper(view: UIView(frame: .zero)) 14 + } 15 + .translationPresentation( 16 + isPresented: $state.isPresented, 17 + text: state.text 18 + ) 19 + } 20 + } 21 + } 22 + 23 + struct UIViewRepresentableWrapper: UIViewRepresentable { 24 + let view: UIView 25 + 26 + func makeUIView(context: Context) -> UIView { 27 + return view 28 + } 29 + 30 + func updateUIView(_ uiView: UIView, context: Context) {} 31 + }
+3
modules/expo-bluesky-translate/src/ExpoBlueskyTranslate.types.ts
··· 1 + export type ExpoBlueskyTranslateModule = { 2 + presentAsync: (text: string) => Promise<void> 3 + }
+48
modules/expo-bluesky-translate/src/ExpoBlueskyTranslateView.ios.tsx
··· 1 + import React from 'react' 2 + import {Platform} from 'react-native' 3 + import {requireNativeModule, requireNativeViewManager} from 'expo-modules-core' 4 + 5 + import {ExpoBlueskyTranslateModule} from './ExpoBlueskyTranslate.types' 6 + 7 + export const NativeTranslationModule = 8 + requireNativeModule<ExpoBlueskyTranslateModule>('ExpoBlueskyTranslate') 9 + 10 + const NativeView: React.ComponentType = requireNativeViewManager( 11 + 'ExpoBlueskyTranslate', 12 + ) 13 + 14 + export function NativeTranslationView() { 15 + return <NativeView /> 16 + } 17 + 18 + export const isAvailable = Number(Platform.Version) >= 17.4 19 + 20 + // https://en.wikipedia.org/wiki/Translate_(Apple)#Languages 21 + const SUPPORTED_LANGUAGES = [ 22 + 'ar', 23 + 'zh', 24 + 'zh', 25 + 'nl', 26 + 'en', 27 + 'en', 28 + 'fr', 29 + 'de', 30 + 'id', 31 + 'it', 32 + 'ja', 33 + 'ko', 34 + 'pl', 35 + 'pt', 36 + 'ru', 37 + 'es', 38 + 'th', 39 + 'tr', 40 + 'uk', 41 + 'vi', 42 + ] 43 + 44 + export function isLanguageSupported(lang?: string) { 45 + // If the language is not provided, we assume it is supported 46 + if (!lang) return true 47 + return SUPPORTED_LANGUAGES.includes(lang) 48 + }
+13
modules/expo-bluesky-translate/src/ExpoBlueskyTranslateView.tsx
··· 1 + export const NativeTranslationModule = { 2 + presentAsync: async (_: string) => {}, 3 + } 4 + 5 + export function NativeTranslationView() { 6 + return null 7 + } 8 + 9 + export const isAvailable = false 10 + 11 + export function isLanguageSupported(_lang?: string) { 12 + return false 13 + }
+2
modules/expo-scroll-forwarder/src/ExpoScrollForwarderView.tsx
··· 1 1 import React from 'react' 2 + 2 3 import {ExpoScrollForwarderViewProps} from './ExpoScrollForwarder.types' 4 + 3 5 export function ExpoScrollForwarderView({ 4 6 children, 5 7 }: React.PropsWithChildren<ExpoScrollForwarderViewProps>) {
+23 -4
src/view/com/post-thread/PostThreadItem.tsx
··· 30 30 import {PostThreadFollowBtn} from 'view/com/post-thread/PostThreadFollowBtn' 31 31 import {atoms as a} from '#/alf' 32 32 import {RichText} from '#/components/RichText' 33 + import { 34 + isAvailable as isNativeTranslationAvailable, 35 + isLanguageSupported, 36 + NativeTranslationModule, 37 + } from '../../../../modules/expo-bluesky-translate' 33 38 import {ContentHider} from '../../../components/moderation/ContentHider' 34 39 import {LabelsOnMyPost} from '../../../components/moderation/LabelsOnMe' 35 40 import {PostAlerts} from '../../../components/moderation/PostAlerts' ··· 317 322 </ContentHider> 318 323 <ExpandedPostDetails 319 324 post={post} 325 + record={record} 320 326 translatorUrl={translatorUrl} 321 327 needsTranslation={needsTranslation} 322 328 /> ··· 620 626 621 627 function ExpandedPostDetails({ 622 628 post, 629 + record, 623 630 needsTranslation, 624 631 translatorUrl, 625 632 }: { 626 633 post: AppBskyFeedDefs.PostView 634 + record?: AppBskyFeedPost.Record 627 635 needsTranslation: boolean 628 636 translatorUrl: string 629 637 }) { 630 638 const pal = usePalette('default') 631 639 const {_} = useLingui() 632 640 const openLink = useOpenLink() 633 - const onTranslatePress = React.useCallback( 634 - () => openLink(translatorUrl), 635 - [openLink, translatorUrl], 636 - ) 641 + 642 + const text = record?.text || '' 643 + 644 + const onTranslatePress = React.useCallback(() => { 645 + if ( 646 + isNativeTranslationAvailable && 647 + isLanguageSupported(record?.langs?.at(0)) 648 + ) { 649 + NativeTranslationModule.presentAsync(text) 650 + } else { 651 + openLink(translatorUrl) 652 + } 653 + }, [openLink, text, translatorUrl, record]) 654 + 637 655 return ( 638 656 <View style={[s.flexRow, s.mt2, s.mb10]}> 639 657 <Text style={pal.textLight}>{niceDate(post.indexedAt)}</Text> 640 658 {needsTranslation && ( 641 659 <> 642 660 <Text style={pal.textLight}> &middot; </Text> 661 + 643 662 <Text 644 663 style={pal.link} 645 664 title={_(msg`Translate`)}
+17 -4
src/view/com/util/forms/PostDropdownBtn.tsx
··· 50 50 import * as Menu from '#/components/Menu' 51 51 import * as Prompt from '#/components/Prompt' 52 52 import {ReportDialog, useReportDialogControl} from '#/components/ReportDialog' 53 + import { 54 + isAvailable as isNativeTranslationAvailable, 55 + isLanguageSupported, 56 + NativeTranslationModule, 57 + } from '../../../../../modules/expo-bluesky-translate' 53 58 import {EventStopper} from '../EventStopper' 54 59 import * as Toast from '../Toast' 55 60 ··· 172 177 Toast.show(_(msg`Copied to clipboard`)) 173 178 }, [_, richText]) 174 179 175 - const onOpenTranslate = React.useCallback(() => { 176 - openLink(translatorUrl) 177 - }, [openLink, translatorUrl]) 180 + const onPressTranslate = React.useCallback(() => { 181 + if ( 182 + isNativeTranslationAvailable && 183 + isLanguageSupported(record?.langs?.at(0)) 184 + ) { 185 + const text = richTextToString(richText, true) 186 + NativeTranslationModule.presentAsync(text) 187 + } else { 188 + openLink(translatorUrl) 189 + } 190 + }, [openLink, record?.langs, richText, translatorUrl]) 178 191 179 192 const onHidePost = React.useCallback(() => { 180 193 hidePost({uri: postUri}) ··· 246 259 <Menu.Item 247 260 testID="postDropdownTranslateBtn" 248 261 label={_(msg`Translate`)} 249 - onPress={onOpenTranslate}> 262 + onPress={onPressTranslate}> 250 263 <Menu.ItemText>{_(msg`Translate`)}</Menu.ItemText> 251 264 <Menu.ItemIcon icon={Translate} position="right" /> 252 265 </Menu.Item>
+2
src/view/shell/index.tsx
··· 33 33 import {MutedWordsDialog} from '#/components/dialogs/MutedWords' 34 34 import {SigninDialog} from '#/components/dialogs/Signin' 35 35 import {Outlet as PortalOutlet} from '#/components/Portal' 36 + import {NativeTranslationView} from '../../../modules/expo-bluesky-translate' 36 37 import {RoutesContainer, TabsNavigator} from '../../Navigation' 37 38 import {Composer} from './Composer' 38 39 import {DrawerContent} from './Drawer' ··· 93 94 </Drawer> 94 95 </ErrorBoundary> 95 96 </Animated.View> 97 + <NativeTranslationView /> 96 98 <Composer winHeight={winDim.height} /> 97 99 <ModalsContainer /> 98 100 <MutedWordsDialog />