Bluesky app fork with some witchin' additions 💫

change translation engine tweak

vs code did some weird auto formatting n that sketches me out

authored by daniela.lol and committed by tangled.org 472772fd 0cdc78b5

+207 -57
+16 -4
src/lib/hooks/useTranslate.ts
··· 1 1 import {useCallback} from 'react' 2 2 import * as IntentLauncher from 'expo-intent-launcher' 3 3 4 - import {getTranslatorLink} from '#/locale/helpers' 4 + import {getTranslatorLink, getTranslatorLinkKagi} from '#/locale/helpers' 5 + import {useTranslationServicePreference} from '#/state/preferences/translation-service-preference' 5 6 import {IS_ANDROID} from '#/env' 6 7 import {useOpenLink} from './useOpenLink' 7 8 8 9 export function useTranslate() { 9 10 const openLink = useOpenLink() 11 + 12 + const translationServicePreference = useTranslationServicePreference() 10 13 11 14 return useCallback( 12 15 async (text: string, language: string) => { 13 - const translateUrl = getTranslatorLink(text, language) 14 - if (IS_ANDROID) { 16 + let translateUrl 17 + 18 + // if ur curious why this isnt a switch case, good question, for some reason making this a switch case breaks the functionality 19 + // it is a mystery https://www.youtube.com/watch?v=fq3abPnEEGE 20 + if (translationServicePreference == 'kagi') { 21 + translateUrl = getTranslatorLinkKagi(text, language) 22 + } else { 23 + translateUrl = getTranslatorLink(text, language) 24 + } 25 + 26 + if (IS_ANDROID && translationServicePreference == 'google') { 15 27 try { 16 28 // use getApplicationIconAsync to determine if the translate app is installed 17 29 if ( ··· 49 61 await openLink(translateUrl) 50 62 } 51 63 }, 52 - [openLink], 64 + [openLink, translationServicePreference], 53 65 ) 54 66 }
+7
src/locale/helpers.ts
··· 127 127 return bcp47Match.basicFilter(lang, targetLangs).length > 0 128 128 } 129 129 130 + // we cant hook into functions like this, so we will make other translator functions n swap between em 130 131 export function getTranslatorLink(text: string, lang: string): string { 131 132 return `https://translate.google.com/?sl=auto&tl=${lang}&text=${encodeURIComponent( 133 + text, 134 + )}` 135 + } 136 + 137 + export function getTranslatorLinkKagi(text: string, lang: string): string { 138 + return `https://translate.kagi.com/?from=auto&to=${lang}&text=${encodeURIComponent( 132 139 text, 133 140 )}` 134 141 }
+24 -5
src/screens/PostThread/components/ThreadItemAnchor.tsx
··· 17 17 import {sanitizeDisplayName} from '#/lib/strings/display-names' 18 18 import {sanitizeHandle} from '#/lib/strings/handles' 19 19 import {niceDate} from '#/lib/strings/time' 20 - import {getTranslatorLink, isPostInLanguage} from '#/locale/helpers' 20 + import { 21 + getTranslatorLink, 22 + getTranslatorLinkKagi, 23 + isPostInLanguage, 24 + } from '#/locale/helpers' 21 25 import { 22 26 POST_TOMBSTONE, 23 27 type Shadow, ··· 31 35 import {useDisableRepostsMetrics} from '#/state/preferences/disable-reposts-metrics' 32 36 import {useDisableSavesMetrics} from '#/state/preferences/disable-saves-metrics' 33 37 import {useEnableSquareButtons} from '#/state/preferences/enable-square-buttons' 38 + import {useTranslationServicePreference} from '#/state/preferences/translation-service-preference' 34 39 import {type ThreadItem} from '#/state/queries/usePostThread/types' 35 40 import {useSession} from '#/state/session' 36 41 import {type OnPostSuccessData} from '#/state/shell/composer' ··· 566 571 const isRootPost = !('reply' in post.record) 567 572 const langPrefs = useLanguagePrefs() 568 573 574 + const translationServicePreference = useTranslationServicePreference() 575 + 569 576 const needsTranslation = useMemo( 570 577 () => 571 578 Boolean( ··· 617 624 <InlineLinkText 618 625 // overridden to open an intent on android, but keep 619 626 // as anchor tag for accessibility 620 - to={getTranslatorLink( 621 - post.record.text, 622 - langPrefs.primaryLanguage, 623 - )} 627 + to={ 628 + // in case u want to expand this to allow other services to be u would do this after the kagi one 629 + // : translationServicePreference === "insert service name" 630 + // ? getTranslatorLink(post.record.text, langPrefs.primaryLanguage, "insert service name") 631 + // atm i cant really think of another service that lets u easily do this so its only kagi for now 632 + // the default itll use if its not any of the checks is google!! 633 + translationServicePreference === 'kagi' 634 + ? getTranslatorLinkKagi( 635 + post.record.text, 636 + langPrefs.primaryLanguage, 637 + ) 638 + : getTranslatorLink( 639 + post.record.text, 640 + langPrefs.primaryLanguage, 641 + ) 642 + } 624 643 label={_(msg`Translate`)} 625 644 style={[a.text_sm]} 626 645 onPress={onTranslatePress}>
+43
src/screens/Settings/DeerSettings.tsx
··· 103 103 useSetShowLinkInHandle, 104 104 useShowLinkInHandle, 105 105 } from '#/state/preferences/show-link-in-handle.tsx' 106 + import { 107 + useSetTranslationServicePreference, 108 + useTranslationServicePreference, 109 + } from '#/state/preferences/translation-service-preference' 106 110 import {useProfilesQuery} from '#/state/queries/profile' 107 111 import * as SettingsList from '#/screens/Settings/components/SettingsList' 108 112 import {atoms as a, useBreakpoints} from '#/alf' ··· 113 117 import {Atom_Stroke2_Corner0_Rounded as DeerIcon} from '#/components/icons/Atom' 114 118 import {ChainLink_Stroke2_Corner0_Rounded as ChainLinkIcon} from '#/components/icons/ChainLink' 115 119 import {Eye_Stroke2_Corner0_Rounded as VisibilityIcon} from '#/components/icons/Eye' 120 + import {Earth_Stroke2_Corner2_Rounded as EarthIcon} from '#/components/icons/Globe' 116 121 import {Lab_Stroke2_Corner0_Rounded as _BeakerIcon} from '#/components/icons/Lab' 117 122 import {PaintRoller_Stroke2_Corner2_Rounded as PaintRollerIcon} from '#/components/icons/PaintRoller' 118 123 import {RaisingHand4Finger_Stroke2_Corner0_Rounded as RaisingHandIcon} from '#/components/icons/RaisingHand' ··· 326 331 327 332 const showLinkInHandle = useShowLinkInHandle() 328 333 const setShowLinkInHandle = useSetShowLinkInHandle() 334 + 335 + const translationServicePreference = useTranslationServicePreference() 336 + const setTranslationServicePreference = useSetTranslationServicePreference() 329 337 330 338 return ( 331 339 <Layout.Screen> ··· 614 622 access to features locked behind email verification. 615 623 </Trans> 616 624 </Admonition> 625 + </SettingsList.Group> 626 + 627 + <SettingsList.Group contentContainerStyle={[a.gap_sm]}> 628 + <SettingsList.ItemIcon icon={EarthIcon} /> 629 + <SettingsList.ItemText> 630 + <Trans>Translation Engine</Trans> 631 + </SettingsList.ItemText> 632 + 633 + <Admonition type="info" style={[a.flex_1]}> 634 + <Trans>Choose the engine to use when translating posts.</Trans> 635 + </Admonition> 636 + 637 + <Toggle.Item 638 + name="service_google" 639 + label={_(msg`Use Google Translate`)} 640 + value={translationServicePreference === 'google'} 641 + onChange={() => setTranslationServicePreference('google')} 642 + style={[a.w_full]}> 643 + <Toggle.LabelText style={[a.flex_1]}> 644 + <Trans>Use Google Translate</Trans> 645 + </Toggle.LabelText> 646 + <Toggle.Radio /> 647 + </Toggle.Item> 648 + 649 + <Toggle.Item 650 + name="service_kagi" 651 + label={_(msg`Use Kagi Translate`)} 652 + value={translationServicePreference === 'kagi'} 653 + onChange={() => setTranslationServicePreference('kagi')} 654 + style={[a.w_full]}> 655 + <Toggle.LabelText style={[a.flex_1]}> 656 + <Trans>Use Kagi Translate</Trans> 657 + </Toggle.LabelText> 658 + <Toggle.Radio /> 659 + </Toggle.Item> 617 660 </SettingsList.Group> 618 661 619 662 <SettingsList.Group contentContainerStyle={[a.gap_sm]}>
+3
src/state/persisted/schema.ts
··· 171 171 172 172 showExternalShareButtons: z.boolean().optional(), 173 173 174 + translationServicePreference: z.enum(['google', 'kagi']), 175 + 174 176 /** @deprecated */ 175 177 mutedThreads: z.array(z.string()), 176 178 trendingDisabled: z.boolean().optional(), ··· 279 281 highQualityImages: false, 280 282 hideUnreplyablePosts: false, 281 283 showExternalShareButtons: false, 284 + translationServicePreference: 'google', 282 285 } 283 286 284 287 export function tryParse(rawData: string): Schema | undefined {
+57 -48
src/state/preferences/index.tsx
··· 37 37 import {Provider as RepostCarouselProvider} from './repost-carousel-enabled' 38 38 import {Provider as ShowLinkInHandleProvider} from './show-link-in-handle' 39 39 import {Provider as SubtitlesProvider} from './subtitles' 40 + import {Provider as TranslationServicePreferenceProvider} from './translation-service-preference' 40 41 import {Provider as TrendingSettingsProvider} from './trending' 41 42 import {Provider as UsedStarterPacksProvider} from './used-starter-packs' 42 43 ··· 59 60 export {useLabelDefinitions} from './label-defs' 60 61 export {useLanguagePrefs, useLanguagePrefsApi} from './languages' 61 62 export {useSetSubtitlesEnabled, useSubtitlesEnabled} from './subtitles' 63 + export { 64 + useSetTranslationServicePreference, 65 + useTranslationServicePreference, 66 + } from './translation-service-preference' 62 67 63 68 export function Provider({children}: React.PropsWithChildren<{}>) { 64 69 return ( ··· 66 71 <AltTextRequiredProvider> 67 72 <ExternalShareButtonsProvider> 68 73 <GoLinksProvider> 69 - <NoAppLabelersProvider> 74 + <NoAppLabelersProvider> 70 75 <DirectFetchRecordsProvider> 71 76 <ConstellationProvider> 72 - <ConstellationInstanceProvider> 73 - <DeerVerificationProvider> 74 - <NoDiscoverProvider> 75 - <ShowLinkInHandleProvider> 77 + <ConstellationInstanceProvider> 78 + <DeerVerificationProvider> 79 + <NoDiscoverProvider> 80 + <ShowLinkInHandleProvider> 76 81 <LargeAltBadgeProvider> 77 82 <ExternalEmbedsProvider> 78 83 <HiddenPostsProvider> 79 - <HighQualityImagesProvider> 84 + <HighQualityImagesProvider> 80 85 <InAppBrowserProvider> 81 86 <DisableHapticsProvider> 82 87 <AutoplayProvider> ··· 84 89 <SubtitlesProvider> 85 90 <TrendingSettingsProvider> 86 91 <RepostCarouselProvider> 87 - <KawaiiProvider> 88 - <HideFeedsPromoTabProvider> 89 - <DisableViaRepostNotificationProvider> 90 - <DisableLikesMetricsProvider> 91 - <DisableRepostsMetricsProvider> 92 - <DisableQuotesMetricsProvider> 93 - <DisableSavesMetricsProvider> 94 - <DisableReplyMetricsProvider> 95 - <DisableFollowersMetricsProvider> 96 - <DisableFollowingMetricsProvider> 97 - <DisableFollowedByMetricsProvider> 98 - <DisablePostsMetricsProvider> 99 - <HideSimilarAccountsRecommProvider> 100 - <HideUnreplyablePostsProvider> 101 - <EnableSquareAvatarsProvider> 102 - <EnableSquareButtonsProvider> 103 - <DisableVerifyEmailReminderProvider> 104 - {children} 105 - </DisableVerifyEmailReminderProvider> 106 - </EnableSquareButtonsProvider> 107 - </EnableSquareAvatarsProvider> 108 - </HideUnreplyablePostsProvider> 109 - </HideSimilarAccountsRecommProvider> 110 - </DisablePostsMetricsProvider> 111 - </DisableFollowedByMetricsProvider> 112 - </DisableFollowingMetricsProvider> 113 - </DisableFollowersMetricsProvider> 114 - </DisableReplyMetricsProvider> 115 - </DisableSavesMetricsProvider> 116 - </DisableQuotesMetricsProvider> 117 - </DisableRepostsMetricsProvider> 118 - </DisableLikesMetricsProvider> 119 - </DisableViaRepostNotificationProvider> 120 - </HideFeedsPromoTabProvider> 121 - </KawaiiProvider> 122 - </RepostCarouselProvider> 92 + <KawaiiProvider> 93 + <HideFeedsPromoTabProvider> 94 + <DisableViaRepostNotificationProvider> 95 + <DisableLikesMetricsProvider> 96 + <DisableRepostsMetricsProvider> 97 + <DisableQuotesMetricsProvider> 98 + <DisableSavesMetricsProvider> 99 + <DisableReplyMetricsProvider> 100 + <DisableFollowersMetricsProvider> 101 + <DisableFollowingMetricsProvider> 102 + <DisableFollowedByMetricsProvider> 103 + <DisablePostsMetricsProvider> 104 + <HideSimilarAccountsRecommProvider> 105 + <HideUnreplyablePostsProvider> 106 + <EnableSquareAvatarsProvider> 107 + <EnableSquareButtonsProvider> 108 + <DisableVerifyEmailReminderProvider> 109 + <TranslationServicePreferenceProvider> 110 + { 111 + children 112 + } 113 + </TranslationServicePreferenceProvider> 114 + </DisableVerifyEmailReminderProvider> 115 + </EnableSquareButtonsProvider> 116 + </EnableSquareAvatarsProvider> 117 + </HideUnreplyablePostsProvider> 118 + </HideSimilarAccountsRecommProvider> 119 + </DisablePostsMetricsProvider> 120 + </DisableFollowedByMetricsProvider> 121 + </DisableFollowingMetricsProvider> 122 + </DisableFollowersMetricsProvider> 123 + </DisableReplyMetricsProvider> 124 + </DisableSavesMetricsProvider> 125 + </DisableQuotesMetricsProvider> 126 + </DisableRepostsMetricsProvider> 127 + </DisableLikesMetricsProvider> 128 + </DisableViaRepostNotificationProvider> 129 + </HideFeedsPromoTabProvider> 130 + </KawaiiProvider> 131 + </RepostCarouselProvider> 123 132 </TrendingSettingsProvider> 124 133 </SubtitlesProvider> 125 134 </UsedStarterPacksProvider> 126 135 </AutoplayProvider> 127 136 </DisableHapticsProvider> 128 137 </InAppBrowserProvider> 129 - </HighQualityImagesProvider> 138 + </HighQualityImagesProvider> 130 139 </HiddenPostsProvider> 131 140 </ExternalEmbedsProvider> 132 141 </LargeAltBadgeProvider> 133 - </ShowLinkInHandleProvider> 134 - </NoDiscoverProvider> 135 - </DeerVerificationProvider> 136 - </ConstellationInstanceProvider> 142 + </ShowLinkInHandleProvider> 143 + </NoDiscoverProvider> 144 + </DeerVerificationProvider> 145 + </ConstellationInstanceProvider> 137 146 </ConstellationProvider> 138 147 </DirectFetchRecordsProvider> 139 - </NoAppLabelersProvider> 148 + </NoAppLabelersProvider> 140 149 </GoLinksProvider> 141 150 </ExternalShareButtonsProvider> 142 151 </AltTextRequiredProvider>
+57
src/state/preferences/translation-service-preference.tsx
··· 1 + import React from 'react' 2 + 3 + import * as persisted from '#/state/persisted' 4 + 5 + type StateContext = persisted.Schema['translationServicePreference'] 6 + type SetContext = (v: persisted.Schema['translationServicePreference']) => void 7 + 8 + const stateContext = React.createContext<StateContext>( 9 + persisted.defaults.translationServicePreference, 10 + ) 11 + const setContext = React.createContext<SetContext>( 12 + (_: persisted.Schema['translationServicePreference']) => {}, 13 + ) 14 + 15 + export function Provider({children}: React.PropsWithChildren<{}>) { 16 + const [state, setState] = React.useState( 17 + persisted.get('translationServicePreference'), 18 + ) 19 + 20 + const setStateWrapped = React.useCallback( 21 + ( 22 + translationServicePreference: persisted.Schema['translationServicePreference'], 23 + ) => { 24 + setState(translationServicePreference) 25 + persisted.write( 26 + 'translationServicePreference', 27 + translationServicePreference, 28 + ) 29 + }, 30 + [setState], 31 + ) 32 + 33 + React.useEffect(() => { 34 + return persisted.onUpdate( 35 + 'translationServicePreference', 36 + nextTranslationServicePreference => { 37 + setState(nextTranslationServicePreference) 38 + }, 39 + ) 40 + }, [setStateWrapped]) 41 + 42 + return ( 43 + <stateContext.Provider value={state}> 44 + <setContext.Provider value={setStateWrapped}> 45 + {children} 46 + </setContext.Provider> 47 + </stateContext.Provider> 48 + ) 49 + } 50 + 51 + export function useTranslationServicePreference() { 52 + return React.useContext(stateContext) 53 + } 54 + 55 + export function useSetTranslationServicePreference() { 56 + return React.useContext(setContext) 57 + }