mirror of
				https://github.com/tooot-app/app
				synced 2025-06-05 22:19:13 +02:00 
			
		
		
		
	Fixed #119 and translation
This commit is contained in:
		@@ -1,5 +1,5 @@
 | 
			
		||||
# [tooot](https://tooot.app/) app for Mastodon
 | 
			
		||||
 | 
			
		||||
[](LICENSE)    [](https://crowdin.tooot.app/project/tooot)
 | 
			
		||||
[](LICENSE)    [](https://crowdin.tooot.app/project/tooot)
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
  
 | 
			
		||||
 
 | 
			
		||||
@@ -19,6 +19,7 @@ public class BasePackageList {
 | 
			
		||||
        new expo.modules.font.FontLoaderPackage(),
 | 
			
		||||
        new expo.modules.haptics.HapticsPackage(),
 | 
			
		||||
        new expo.modules.imageloader.ImageLoaderPackage(),
 | 
			
		||||
        new expo.modules.imagemanipulator.ImageManipulatorPackage(),
 | 
			
		||||
        new expo.modules.imagepicker.ImagePickerPackage(),
 | 
			
		||||
        new expo.modules.keepawake.KeepAwakePackage(),
 | 
			
		||||
        new expo.modules.localization.LocalizationPackage(),
 | 
			
		||||
 
 | 
			
		||||
@@ -825,7 +825,7 @@ SPEC CHECKSUMS:
 | 
			
		||||
  EXVideoThumbnails: cd257fc6e07884a704a5674d362a6410933acb68
 | 
			
		||||
  EXWebBrowser: 0b466c50e5ff61c9758095d49d5081e3229d77ac
 | 
			
		||||
  FBLazyVector: 7b423f9e248eae65987838148c36eec1dbfe0b53
 | 
			
		||||
  FBReactNativeSpec: be55984d4a593b4ef281ead81139cdfb1812d259
 | 
			
		||||
  FBReactNativeSpec: 5058d1917c80dca4b9ed89bdf94385315939ab80
 | 
			
		||||
  Firebase: cd2ab85eec8170dc260186159f21072ecb679ad5
 | 
			
		||||
  FirebaseAnalytics: f3f8f75de34fe04141a69bb1c4bd7e24a80178e1
 | 
			
		||||
  FirebaseCore: ac35d680a0bf32319a59966a1478e0741536b97b
 | 
			
		||||
 
 | 
			
		||||
@@ -346,7 +346,7 @@
 | 
			
		||||
				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 | 
			
		||||
				CLANG_ENABLE_MODULES = YES;
 | 
			
		||||
				CODE_SIGN_ENTITLEMENTS = tooot/tooot.entitlements;
 | 
			
		||||
				CODE_SIGN_IDENTITY = "iPhone Developer";
 | 
			
		||||
				CODE_SIGN_IDENTITY = "iPhone Distribution";
 | 
			
		||||
				CODE_SIGN_STYLE = Manual;
 | 
			
		||||
				CURRENT_PROJECT_VERSION = 2102022230;
 | 
			
		||||
				DEVELOPMENT_TEAM = 8EGBLQ2MA6;
 | 
			
		||||
@@ -366,7 +366,7 @@
 | 
			
		||||
				);
 | 
			
		||||
				PRODUCT_BUNDLE_IDENTIFIER = com.xmflsct.app.tooot;
 | 
			
		||||
				PRODUCT_NAME = tooot;
 | 
			
		||||
				PROVISIONING_PROFILE_SPECIFIER = "match Development com.xmflsct.app.tooot";
 | 
			
		||||
				PROVISIONING_PROFILE_SPECIFIER = "match AdHoc com.xmflsct.app.tooot";
 | 
			
		||||
				SWIFT_OBJC_BRIDGING_HEADER = "tooot-Bridging-Header.h";
 | 
			
		||||
				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 | 
			
		||||
				SWIFT_VERSION = 5.0;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										15
									
								
								src/@types/translate.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										15
									
								
								src/@types/translate.d.ts
									
									
									
									
										vendored
									
									
								
							@@ -1,15 +0,0 @@
 | 
			
		||||
declare namespace Translate {
 | 
			
		||||
  type Detect = {
 | 
			
		||||
    confidence: number
 | 
			
		||||
    language: string
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  type Language = {
 | 
			
		||||
    code: string
 | 
			
		||||
    name: string
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  type Translate = {
 | 
			
		||||
    translatedText: string
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -74,7 +74,11 @@ const apiGeneral = async <T = unknown>({
 | 
			
		||||
        // The request was made but no response was received
 | 
			
		||||
        // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
 | 
			
		||||
        // http.ClientRequest in node.js
 | 
			
		||||
        console.error(ctx.bold(' API general '), ctx.bold('request'), error)
 | 
			
		||||
        console.error(
 | 
			
		||||
          ctx.bold(' API general '),
 | 
			
		||||
          ctx.bold('request'),
 | 
			
		||||
          error.request
 | 
			
		||||
        )
 | 
			
		||||
        return Promise.reject()
 | 
			
		||||
      } else {
 | 
			
		||||
        console.error(
 | 
			
		||||
 
 | 
			
		||||
@@ -76,7 +76,7 @@ const MenuRow: React.FC<Props> = ({
 | 
			
		||||
          }
 | 
			
		||||
        }}
 | 
			
		||||
      >
 | 
			
		||||
        <View>
 | 
			
		||||
        <View style={{ flex: 1 }}>
 | 
			
		||||
          <View style={styles.core}>
 | 
			
		||||
            <View style={styles.front}>
 | 
			
		||||
              {iconFront && (
 | 
			
		||||
 
 | 
			
		||||
@@ -164,6 +164,7 @@ export interface Props {
 | 
			
		||||
  expandHint?: string
 | 
			
		||||
  highlighted?: boolean
 | 
			
		||||
  disableDetails?: boolean
 | 
			
		||||
  selectable?: boolean
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const ParseHTML = React.memo(
 | 
			
		||||
@@ -178,7 +179,8 @@ const ParseHTML = React.memo(
 | 
			
		||||
    numberOfLines = 10,
 | 
			
		||||
    expandHint,
 | 
			
		||||
    highlighted = false,
 | 
			
		||||
    disableDetails = false
 | 
			
		||||
    disableDetails = false,
 | 
			
		||||
    selectable = false
 | 
			
		||||
  }: Props) => {
 | 
			
		||||
    const adaptiveFontsize = useSelector(getSettingsFontsize)
 | 
			
		||||
    const adaptedFontsize = adaptiveScale(
 | 
			
		||||
@@ -255,6 +257,7 @@ const ParseHTML = React.memo(
 | 
			
		||||
              numberOfLines={
 | 
			
		||||
                expandAllow ? (expanded ? 999 : numberOfLines) : undefined
 | 
			
		||||
              }
 | 
			
		||||
              selectable={selectable}
 | 
			
		||||
            />
 | 
			
		||||
            {expandAllow ? (
 | 
			
		||||
              <Pressable
 | 
			
		||||
 
 | 
			
		||||
@@ -32,6 +32,7 @@ const TimelineContent = React.memo(
 | 
			
		||||
              numberOfLines={999}
 | 
			
		||||
              highlighted={highlighted}
 | 
			
		||||
              disableDetails={disableDetails}
 | 
			
		||||
              selectable={highlighted}
 | 
			
		||||
            />
 | 
			
		||||
            <ParseHTML
 | 
			
		||||
              content={status.content}
 | 
			
		||||
@@ -44,6 +45,7 @@ const TimelineContent = React.memo(
 | 
			
		||||
              expandHint={t('shared.content.expandHint')}
 | 
			
		||||
              highlighted={highlighted}
 | 
			
		||||
              disableDetails={disableDetails}
 | 
			
		||||
              selectable={highlighted}
 | 
			
		||||
            />
 | 
			
		||||
          </>
 | 
			
		||||
        ) : (
 | 
			
		||||
@@ -56,6 +58,7 @@ const TimelineContent = React.memo(
 | 
			
		||||
            tags={status.tags}
 | 
			
		||||
            numberOfLines={highlighted ? 999 : numberOfLines}
 | 
			
		||||
            disableDetails={disableDetails}
 | 
			
		||||
            selectable={highlighted}
 | 
			
		||||
          />
 | 
			
		||||
        )}
 | 
			
		||||
      </>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,32 +1,16 @@
 | 
			
		||||
import analytics from '@components/analytics'
 | 
			
		||||
import { ParseHTML } from '@components/Parse'
 | 
			
		||||
import { useTranslateQuery } from '@utils/queryHooks/translate'
 | 
			
		||||
import { getInstanceUri } from '@utils/slices/instancesSlice'
 | 
			
		||||
import { getSettingsLanguage } from '@utils/slices/settingsSlice'
 | 
			
		||||
import { StyleConstants } from '@utils/styles/constants'
 | 
			
		||||
import { useTheme } from '@utils/styles/ThemeManager'
 | 
			
		||||
import htmlparser2 from 'htmlparser2-without-node-native'
 | 
			
		||||
import React, { useState } from 'react'
 | 
			
		||||
import { useTranslation } from 'react-i18next'
 | 
			
		||||
import { Pressable, StyleSheet, Text } from 'react-native'
 | 
			
		||||
import { Circle } from 'react-native-animated-spinkit'
 | 
			
		||||
import { useSelector } from 'react-redux'
 | 
			
		||||
 | 
			
		||||
const availableLanguages = [
 | 
			
		||||
  'en',
 | 
			
		||||
  'ar',
 | 
			
		||||
  'zh',
 | 
			
		||||
  'fr',
 | 
			
		||||
  'de',
 | 
			
		||||
  'hi',
 | 
			
		||||
  'ga',
 | 
			
		||||
  'it',
 | 
			
		||||
  'ja',
 | 
			
		||||
  'ko',
 | 
			
		||||
  'pl',
 | 
			
		||||
  'pt',
 | 
			
		||||
  'ru',
 | 
			
		||||
  'es',
 | 
			
		||||
  'tr'
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
export interface Props {
 | 
			
		||||
  highlighted: boolean
 | 
			
		||||
  status: Mastodon.Status
 | 
			
		||||
@@ -45,48 +29,31 @@ const TimelineTranslate = React.memo(
 | 
			
		||||
    const { theme } = useTheme()
 | 
			
		||||
 | 
			
		||||
    const tootLanguage = status.language.slice(0, 2)
 | 
			
		||||
    if (!availableLanguages.includes(tootLanguage)) {
 | 
			
		||||
      return (
 | 
			
		||||
        <Text
 | 
			
		||||
          style={{
 | 
			
		||||
            ...StyleConstants.FontStyle.M,
 | 
			
		||||
            color: theme.disabled
 | 
			
		||||
          }}
 | 
			
		||||
        >
 | 
			
		||||
          {t('shared.translate.unavailable')}
 | 
			
		||||
        </Text>
 | 
			
		||||
      )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const settingsLanguage = useSelector(getSettingsLanguage, () => true)
 | 
			
		||||
    const settingsLanguage = useSelector(getSettingsLanguage)
 | 
			
		||||
 | 
			
		||||
    if (settingsLanguage.includes(tootLanguage)) {
 | 
			
		||||
      return null
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let emojisRemoved = status.spoiler_text
 | 
			
		||||
      ? status.spoiler_text.concat(status.content)
 | 
			
		||||
      : status.content
 | 
			
		||||
    if (status.emojis) {
 | 
			
		||||
    let text = status.spoiler_text
 | 
			
		||||
      ? [status.spoiler_text, status.content]
 | 
			
		||||
      : [status.content]
 | 
			
		||||
 | 
			
		||||
    for (const i in text) {
 | 
			
		||||
      for (const emoji of status.emojis) {
 | 
			
		||||
        emojisRemoved = emojisRemoved.replaceAll(`:${emoji.shortcode}:`, '')
 | 
			
		||||
        text[i] = text[i].replaceAll(`:${emoji.shortcode}:`, '')
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let cleaned = ''
 | 
			
		||||
    const parser = new htmlparser2.Parser({
 | 
			
		||||
      ontext (text: string) {
 | 
			
		||||
        cleaned = cleaned.concat(text)
 | 
			
		||||
      }
 | 
			
		||||
    })
 | 
			
		||||
    parser.write(emojisRemoved)
 | 
			
		||||
    parser.end()
 | 
			
		||||
 | 
			
		||||
    const instanceUri = useSelector(getInstanceUri)
 | 
			
		||||
    const [enabled, setEnabled] = useState(false)
 | 
			
		||||
    const { refetch, data, isLoading, isSuccess, isError } = useTranslateQuery({
 | 
			
		||||
      toot: cleaned,
 | 
			
		||||
      instance: instanceUri!,
 | 
			
		||||
      id: status.id,
 | 
			
		||||
      source: status.language,
 | 
			
		||||
      target: settingsLanguage.slice(0, 2),
 | 
			
		||||
      target: settingsLanguage,
 | 
			
		||||
      text,
 | 
			
		||||
      options: { enabled }
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
@@ -97,9 +64,15 @@ const TimelineTranslate = React.memo(
 | 
			
		||||
          onPress={() => {
 | 
			
		||||
            if (enabled) {
 | 
			
		||||
              if (!isSuccess) {
 | 
			
		||||
                analytics('timeline_shared_translate_retry', {
 | 
			
		||||
                  language: status.language
 | 
			
		||||
                })
 | 
			
		||||
                refetch()
 | 
			
		||||
              }
 | 
			
		||||
            } else {
 | 
			
		||||
              analytics('timeline_shared_translate', {
 | 
			
		||||
                language: status.language
 | 
			
		||||
              })
 | 
			
		||||
              setEnabled(true)
 | 
			
		||||
            }
 | 
			
		||||
          }}
 | 
			
		||||
@@ -109,15 +82,21 @@ const TimelineTranslate = React.memo(
 | 
			
		||||
              ...StyleConstants.FontStyle.M,
 | 
			
		||||
              color:
 | 
			
		||||
                isLoading || isSuccess
 | 
			
		||||
                  ? theme.disabled
 | 
			
		||||
                  ? theme.secondary
 | 
			
		||||
                  : isError
 | 
			
		||||
                  ? theme.red
 | 
			
		||||
                  : theme.blue
 | 
			
		||||
            }}
 | 
			
		||||
          >
 | 
			
		||||
            {isError
 | 
			
		||||
              ? t('shared.translate.error')
 | 
			
		||||
              ? t('shared.translate.failed')
 | 
			
		||||
              : isSuccess
 | 
			
		||||
              ? t('shared.translate.succeed', {
 | 
			
		||||
                  provider: data?.provider,
 | 
			
		||||
                  source: data?.sourceLanguage
 | 
			
		||||
                })
 | 
			
		||||
              : t('shared.translate.default')}
 | 
			
		||||
            {__DEV__ ? ` Source: ${status.language}` : undefined}
 | 
			
		||||
          </Text>
 | 
			
		||||
          {isLoading ? (
 | 
			
		||||
            <Circle
 | 
			
		||||
@@ -127,15 +106,17 @@ const TimelineTranslate = React.memo(
 | 
			
		||||
            />
 | 
			
		||||
          ) : null}
 | 
			
		||||
        </Pressable>
 | 
			
		||||
        {data ? (
 | 
			
		||||
          <Text
 | 
			
		||||
            style={{
 | 
			
		||||
              ...StyleConstants.FontStyle.M,
 | 
			
		||||
              color: theme.primaryDefault
 | 
			
		||||
            }}
 | 
			
		||||
            children={data}
 | 
			
		||||
          />
 | 
			
		||||
        ) : null}
 | 
			
		||||
        {data
 | 
			
		||||
          ? data.text.map((d, i) => (
 | 
			
		||||
              <ParseHTML
 | 
			
		||||
                key={i}
 | 
			
		||||
                content={d}
 | 
			
		||||
                size={'M'}
 | 
			
		||||
                numberOfLines={999}
 | 
			
		||||
                selectable
 | 
			
		||||
              />
 | 
			
		||||
            ))
 | 
			
		||||
          : null}
 | 
			
		||||
      </>
 | 
			
		||||
    )
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -76,8 +76,8 @@
 | 
			
		||||
    "fullConversation": "Read conversations",
 | 
			
		||||
    "translate": {
 | 
			
		||||
      "default": "Translate",
 | 
			
		||||
      "error": "Try to translate again",
 | 
			
		||||
      "unavailable": "Language not supported"
 | 
			
		||||
      "succeed": "Translated by {{provider}} from {{source}}",
 | 
			
		||||
      "failed": "Translation failed"
 | 
			
		||||
    },
 | 
			
		||||
    "header": {
 | 
			
		||||
      "shared": {
 | 
			
		||||
 
 | 
			
		||||
@@ -87,6 +87,7 @@ const ScreenAnnouncements: React.FC<ScreenAnnouncementsProp> = ({
 | 
			
		||||
              emojis={item.emojis}
 | 
			
		||||
              mentions={item.mentions}
 | 
			
		||||
              numberOfLines={999}
 | 
			
		||||
              selectable
 | 
			
		||||
            />
 | 
			
		||||
          </ScrollView>
 | 
			
		||||
          {item.reactions?.length ? (
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import analytics from '@components/analytics'
 | 
			
		||||
import { MenuContainer, MenuRow } from '@components/Menu'
 | 
			
		||||
import { displayMessage } from '@components/Message'
 | 
			
		||||
import { useActionSheet } from '@expo/react-native-action-sheet'
 | 
			
		||||
@@ -40,6 +41,12 @@ const TabMeProfileRoot: React.FC<StackScreenProps<
 | 
			
		||||
      async buttonIndex => {
 | 
			
		||||
        switch (buttonIndex) {
 | 
			
		||||
          case 0:
 | 
			
		||||
            analytics('me_profile_visibility', {
 | 
			
		||||
              current: t(
 | 
			
		||||
                `me.profile.root.visibility.options.${data?.source.privacy}`
 | 
			
		||||
              ),
 | 
			
		||||
              new: 'public'
 | 
			
		||||
            })
 | 
			
		||||
            mutateAsync({ type: 'source[privacy]', data: 'public' })
 | 
			
		||||
              .then(() => dispatch(updateAccountPreferences()))
 | 
			
		||||
              .catch(err =>
 | 
			
		||||
@@ -55,6 +62,12 @@ const TabMeProfileRoot: React.FC<StackScreenProps<
 | 
			
		||||
              )
 | 
			
		||||
            break
 | 
			
		||||
          case 1:
 | 
			
		||||
            analytics('me_profile_visibility', {
 | 
			
		||||
              current: t(
 | 
			
		||||
                `me.profile.root.visibility.options.${data?.source.privacy}`
 | 
			
		||||
              ),
 | 
			
		||||
              new: 'unlisted'
 | 
			
		||||
            })
 | 
			
		||||
            mutateAsync({ type: 'source[privacy]', data: 'unlisted' })
 | 
			
		||||
              .then(() => dispatch(updateAccountPreferences()))
 | 
			
		||||
              .catch(err =>
 | 
			
		||||
@@ -70,6 +83,12 @@ const TabMeProfileRoot: React.FC<StackScreenProps<
 | 
			
		||||
              )
 | 
			
		||||
            break
 | 
			
		||||
          case 2:
 | 
			
		||||
            analytics('me_profile_visibility', {
 | 
			
		||||
              current: t(
 | 
			
		||||
                `me.profile.root.visibility.options.${data?.source.privacy}`
 | 
			
		||||
              ),
 | 
			
		||||
              new: 'unlisted'
 | 
			
		||||
            })
 | 
			
		||||
            mutateAsync({ type: 'source[privacy]', data: 'private' })
 | 
			
		||||
              .then(() => dispatch(updateAccountPreferences()))
 | 
			
		||||
              .catch(err =>
 | 
			
		||||
@@ -87,10 +106,14 @@ const TabMeProfileRoot: React.FC<StackScreenProps<
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
    )
 | 
			
		||||
  }, [])
 | 
			
		||||
  }, [data?.source.privacy])
 | 
			
		||||
 | 
			
		||||
  const onPressSensitive = useCallback(() => {
 | 
			
		||||
    if (data?.source.sensitive === undefined) {
 | 
			
		||||
      analytics('me_profile_sensitive', {
 | 
			
		||||
        current: undefined,
 | 
			
		||||
        new: true
 | 
			
		||||
      })
 | 
			
		||||
      mutateAsync({ type: 'source[sensitive]', data: true })
 | 
			
		||||
        .then(() => dispatch(updateAccountPreferences()))
 | 
			
		||||
        .catch(err =>
 | 
			
		||||
@@ -105,6 +128,10 @@ const TabMeProfileRoot: React.FC<StackScreenProps<
 | 
			
		||||
          })
 | 
			
		||||
        )
 | 
			
		||||
    } else {
 | 
			
		||||
      analytics('me_profile_sensitive', {
 | 
			
		||||
        current: data.source.sensitive,
 | 
			
		||||
        new: !data.source.sensitive
 | 
			
		||||
      })
 | 
			
		||||
      mutateAsync({
 | 
			
		||||
        type: 'source[sensitive]',
 | 
			
		||||
        data: !data.source.sensitive
 | 
			
		||||
@@ -126,6 +153,10 @@ const TabMeProfileRoot: React.FC<StackScreenProps<
 | 
			
		||||
 | 
			
		||||
  const onPressLock = useCallback(() => {
 | 
			
		||||
    if (data?.locked === undefined) {
 | 
			
		||||
      analytics('me_profile_lock', {
 | 
			
		||||
        current: undefined,
 | 
			
		||||
        new: true
 | 
			
		||||
      })
 | 
			
		||||
      mutateAsync({ type: 'locked', data: true }).catch(err =>
 | 
			
		||||
        displayMessage({
 | 
			
		||||
          ref: messageRef,
 | 
			
		||||
@@ -138,6 +169,10 @@ const TabMeProfileRoot: React.FC<StackScreenProps<
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
    } else {
 | 
			
		||||
      analytics('me_profile_lock', {
 | 
			
		||||
        current: data.locked,
 | 
			
		||||
        new: !data.locked
 | 
			
		||||
      })
 | 
			
		||||
      mutateAsync({ type: 'locked', data: !data.locked }).catch(err =>
 | 
			
		||||
        displayMessage({
 | 
			
		||||
          ref: messageRef,
 | 
			
		||||
@@ -154,6 +189,10 @@ const TabMeProfileRoot: React.FC<StackScreenProps<
 | 
			
		||||
 | 
			
		||||
  const onPressBot = useCallback(() => {
 | 
			
		||||
    if (data?.bot === undefined) {
 | 
			
		||||
      analytics('me_profile_bot', {
 | 
			
		||||
        current: undefined,
 | 
			
		||||
        new: true
 | 
			
		||||
      })
 | 
			
		||||
      mutateAsync({ type: 'bot', data: true }).catch(err =>
 | 
			
		||||
        displayMessage({
 | 
			
		||||
          ref: messageRef,
 | 
			
		||||
@@ -166,6 +205,10 @@ const TabMeProfileRoot: React.FC<StackScreenProps<
 | 
			
		||||
        })
 | 
			
		||||
      )
 | 
			
		||||
    } else {
 | 
			
		||||
      analytics('me_profile_bot', {
 | 
			
		||||
        current: data.bot,
 | 
			
		||||
        new: !data.bot
 | 
			
		||||
      })
 | 
			
		||||
      mutateAsync({ type: 'bot', data: !data?.bot }).catch(err =>
 | 
			
		||||
        displayMessage({
 | 
			
		||||
          ref: messageRef,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,4 @@
 | 
			
		||||
import analytics from '@components/analytics'
 | 
			
		||||
import Button from '@components/Button'
 | 
			
		||||
import { MenuContainer, MenuRow } from '@components/Menu'
 | 
			
		||||
import { updateInstancePush } from '@utils/slices/instances/updatePush'
 | 
			
		||||
@@ -67,7 +68,11 @@ const TabMePush: React.FC = () => {
 | 
			
		||||
              !pushEnabled || !instancePush.global.value || isLoading
 | 
			
		||||
            }
 | 
			
		||||
            switchValue={instancePush?.alerts[alert].value}
 | 
			
		||||
            switchOnValueChange={() =>
 | 
			
		||||
            switchOnValueChange={() => {
 | 
			
		||||
              analytics(`me_push_${alert}`, {
 | 
			
		||||
                current: instancePush?.alerts[alert].value,
 | 
			
		||||
                new: !instancePush?.alerts[alert].value
 | 
			
		||||
              })
 | 
			
		||||
              dispatch(
 | 
			
		||||
                updateInstancePushAlert({
 | 
			
		||||
                  changed: alert,
 | 
			
		||||
@@ -80,7 +85,7 @@ const TabMePush: React.FC = () => {
 | 
			
		||||
                  }
 | 
			
		||||
                })
 | 
			
		||||
              )
 | 
			
		||||
            }
 | 
			
		||||
            }}
 | 
			
		||||
          />
 | 
			
		||||
        ))
 | 
			
		||||
      : null
 | 
			
		||||
@@ -103,10 +108,12 @@ const TabMePush: React.FC = () => {
 | 
			
		||||
            }}
 | 
			
		||||
            onPress={async () => {
 | 
			
		||||
              if (pushCanAskAgain) {
 | 
			
		||||
                analytics('me_push_enabled_dialogue')
 | 
			
		||||
                const result = await Notifications.requestPermissionsAsync()
 | 
			
		||||
                setPushEnabled(result.granted)
 | 
			
		||||
                setPushCanAskAgain(result.canAskAgain)
 | 
			
		||||
              } else {
 | 
			
		||||
                analytics('me_push_enabled_setting')
 | 
			
		||||
                Linking.openSettings()
 | 
			
		||||
              }
 | 
			
		||||
            }}
 | 
			
		||||
@@ -124,9 +131,13 @@ const TabMePush: React.FC = () => {
 | 
			
		||||
          switchValue={
 | 
			
		||||
            pushEnabled === false ? false : instancePush?.global.value
 | 
			
		||||
          }
 | 
			
		||||
          switchOnValueChange={() =>
 | 
			
		||||
          switchOnValueChange={() => {
 | 
			
		||||
            analytics('me_push_global', {
 | 
			
		||||
              current: instancePush?.global.value,
 | 
			
		||||
              new: !instancePush?.global.value
 | 
			
		||||
            })
 | 
			
		||||
            dispatch(updateInstancePush(!instancePush?.global.value))
 | 
			
		||||
          }
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      </MenuContainer>
 | 
			
		||||
      <MenuContainer>
 | 
			
		||||
@@ -138,16 +149,21 @@ const TabMePush: React.FC = () => {
 | 
			
		||||
            !pushEnabled || !instancePush?.global.value || isLoading
 | 
			
		||||
          }
 | 
			
		||||
          switchValue={instancePush?.decode.value}
 | 
			
		||||
          switchOnValueChange={() =>
 | 
			
		||||
          switchOnValueChange={() => {
 | 
			
		||||
            analytics('me_push_decode', {
 | 
			
		||||
              current: instancePush?.decode.value,
 | 
			
		||||
              new: !instancePush?.decode.value
 | 
			
		||||
            })
 | 
			
		||||
            dispatch(updateInstancePushDecode(!instancePush?.decode.value))
 | 
			
		||||
          }
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
        <MenuRow
 | 
			
		||||
          title={t('me.push.howitworks')}
 | 
			
		||||
          iconBack='ExternalLink'
 | 
			
		||||
          onPress={() =>
 | 
			
		||||
          onPress={() => {
 | 
			
		||||
            analytics('me_push_howitworks')
 | 
			
		||||
            WebBrowser.openBrowserAsync('https://tooot.app/how-push-works')
 | 
			
		||||
          }
 | 
			
		||||
          }}
 | 
			
		||||
        />
 | 
			
		||||
      </MenuContainer>
 | 
			
		||||
      <MenuContainer>{alerts}</MenuContainer>
 | 
			
		||||
 
 | 
			
		||||
@@ -34,6 +34,7 @@ const AccountInformationFields = React.memo(
 | 
			
		||||
                emojis={account.emojis}
 | 
			
		||||
                showFullLink
 | 
			
		||||
                numberOfLines={5}
 | 
			
		||||
                selectable
 | 
			
		||||
              />
 | 
			
		||||
              {field.verified_at ? (
 | 
			
		||||
                <Icon
 | 
			
		||||
@@ -51,6 +52,7 @@ const AccountInformationFields = React.memo(
 | 
			
		||||
                emojis={account.emojis}
 | 
			
		||||
                showFullLink
 | 
			
		||||
                numberOfLines={5}
 | 
			
		||||
                selectable
 | 
			
		||||
              />
 | 
			
		||||
            </View>
 | 
			
		||||
          </View>
 | 
			
		||||
 
 | 
			
		||||
@@ -28,7 +28,7 @@ const AccountInformationNote = React.memo(
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <View style={styles.note}>
 | 
			
		||||
        <ParseHTML content={account.note!} size={'M'} emojis={account.emojis} />
 | 
			
		||||
        <ParseHTML content={account.note!} size={'M'} emojis={account.emojis} selectable />
 | 
			
		||||
      </View>
 | 
			
		||||
    )
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,39 +1,55 @@
 | 
			
		||||
import apiGeneral from '@api/general'
 | 
			
		||||
import { AxiosError } from 'axios'
 | 
			
		||||
import { Constants } from 'react-native-unimodules'
 | 
			
		||||
import { Buffer } from 'buffer'
 | 
			
		||||
import Constants from 'expo-constants'
 | 
			
		||||
import { useQuery, UseQueryOptions } from 'react-query'
 | 
			
		||||
 | 
			
		||||
type Translations = {
 | 
			
		||||
  provider: string
 | 
			
		||||
  sourceLanguage: string
 | 
			
		||||
  text: string[]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export type QueryKeyTranslate = [
 | 
			
		||||
  'Translate',
 | 
			
		||||
  { toot: string; source: string; target: string }
 | 
			
		||||
  {
 | 
			
		||||
    instance: string
 | 
			
		||||
    id: string
 | 
			
		||||
    source: string
 | 
			
		||||
    target: string
 | 
			
		||||
    text: string[]
 | 
			
		||||
  }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
const queryFunction = async ({ queryKey }: { queryKey: QueryKeyTranslate }) => {
 | 
			
		||||
  const { toot, source, target } = queryKey[1]
 | 
			
		||||
export const TRANSLATE_SERVER = __DEV__
 | 
			
		||||
  ? 'testtranslate.tooot.app'
 | 
			
		||||
  : 'translate.tooot.app'
 | 
			
		||||
 | 
			
		||||
  const res = await apiGeneral<Translate.Translate>({
 | 
			
		||||
    domain: 'translate.tooot.app',
 | 
			
		||||
    method: 'post',
 | 
			
		||||
    url: 'translate',
 | 
			
		||||
    params: {
 | 
			
		||||
      api_key: Constants.manifest?.extra?.translateKey,
 | 
			
		||||
      q: toot,
 | 
			
		||||
      source,
 | 
			
		||||
      target
 | 
			
		||||
const queryFunction = async ({ queryKey }: { queryKey: QueryKeyTranslate }) => {
 | 
			
		||||
  const key = Constants.manifest.extra?.translateKey
 | 
			
		||||
  if (!key) {
 | 
			
		||||
    return Promise.reject()
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const { instance, id, source, target, text } = queryKey[1]
 | 
			
		||||
 | 
			
		||||
  const res = await apiGeneral<Translations>({
 | 
			
		||||
    domain: TRANSLATE_SERVER,
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    url: `v1/translate/${instance}/${id}/${target}`,
 | 
			
		||||
    headers: {
 | 
			
		||||
      key,
 | 
			
		||||
      original: Buffer.from(JSON.stringify({ source, text })).toString('base64')
 | 
			
		||||
    }
 | 
			
		||||
  })
 | 
			
		||||
  return res.body.translatedText
 | 
			
		||||
  return res.body
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const useTranslateQuery = ({
 | 
			
		||||
  options,
 | 
			
		||||
  ...queryKeyParams
 | 
			
		||||
}: QueryKeyTranslate[1] & {
 | 
			
		||||
  options?: UseQueryOptions<
 | 
			
		||||
    Translate.Translate['translatedText'],
 | 
			
		||||
    AxiosError,
 | 
			
		||||
    Translate.Translate['translatedText']
 | 
			
		||||
  >
 | 
			
		||||
  options?: UseQueryOptions<Translations, AxiosError, Translations>
 | 
			
		||||
}) => {
 | 
			
		||||
  const queryKey: QueryKeyTranslate = ['Translate', { ...queryKeyParams }]
 | 
			
		||||
  return useQuery(queryKey, queryFunction, options)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user