diff --git a/src/App.tsx b/src/App.tsx index c1d671e2..a5cb9969 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,6 +2,7 @@ import { ActionSheetProvider } from '@expo/react-native-action-sheet' import * as Sentry from '@sentry/react-native' import { QueryClientProvider } from '@tanstack/react-query' import AccessibilityManager from '@utils/accessibility/AccessibilityManager' +import { connectVerify } from '@utils/api/helpers/connect' import getLanguage from '@utils/helpers/getLanguage' import { queryClient } from '@utils/queryHooks' import audio from '@utils/startup/audio' @@ -50,20 +51,28 @@ const App: React.FC = () => { await migrateFromAsyncStorage() setHasMigrated(true) } catch {} + } + + const useConnect = getGlobalStorage.boolean('app.connect') + log('log', 'App', `connect: ${useConnect}`) + if (useConnect) { + await connectVerify() + .then(() => log('log', 'App', 'connected')) + .catch(() => log('warn', 'App', 'connect verify failed')) + } + + log('log', 'App', 'loading from MMKV') + const account = getGlobalStorage.string('account.active') + if (account) { + await setAccount(account) } else { - log('log', 'App', 'loading from MMKV') - const account = getGlobalStorage.string('account.active') - if (account) { - await setAccount(account) + log('log', 'App', 'No active account available') + const accounts = getGlobalStorage.object('accounts') + if (accounts?.length) { + log('log', 'App', `Setting active account ${accounts[accounts.length - 1]}`) + await setAccount(accounts[accounts.length - 1]) } else { - log('log', 'App', 'No active account available') - const accounts = getGlobalStorage.object('accounts') - if (accounts?.length) { - log('log', 'App', `Setting active account ${accounts[accounts.length - 1]}`) - await setAccount(accounts[accounts.length - 1]) - } else { - setGlobalStorage('account.active', undefined) - } + setGlobalStorage('account.active', undefined) } } diff --git a/src/components/Emojis/List.tsx b/src/components/Emojis/List.tsx index e8d2f953..6d72be23 100644 --- a/src/components/Emojis/List.tsx +++ b/src/components/Emojis/List.tsx @@ -2,6 +2,7 @@ import { emojis } from '@components/Emojis' import Icon from '@components/Icon' import CustomText from '@components/Text' import { useAccessibility } from '@utils/accessibility/AccessibilityManager' +import { connectImage } from '@utils/api/helpers/connect' import { StorageAccount } from '@utils/storage/account' import { getAccountStorage, setAccountStorage } from '@utils/storage/actions' import { StyleConstants } from '@utils/styles/constants' @@ -133,7 +134,7 @@ const EmojisList = () => { emoji: emoji.shortcode })} accessibilityHint={t('screenCompose:content.root.footer.emojis.accessibilityHint')} - source={{ uri }} + source={connectImage({ uri })} style={{ width: 32, height: 32 }} /> diff --git a/src/components/GracefullyImage.tsx b/src/components/GracefullyImage.tsx index 3ad17fbf..9a708980 100644 --- a/src/components/GracefullyImage.tsx +++ b/src/components/GracefullyImage.tsx @@ -1,4 +1,5 @@ import { useAccessibility } from '@utils/accessibility/AccessibilityManager' +import { connectImage } from '@utils/api/helpers/connect' import { useTheme } from '@utils/styles/ThemeManager' import React, { useEffect, useState } from 'react' import { @@ -56,7 +57,7 @@ const GracefullyImage = ({ const [imageLoaded, setImageLoaded] = useState(false) const [currentUri, setCurrentUri] = useState(uri.original || uri.remote) - const source = { + const source: { uri?: string } = { uri: reduceMotionEnabled && uri.static ? uri.static : currentUri } useEffect(() => { @@ -90,12 +91,12 @@ const GracefullyImage = ({ > {uri.preview && !imageLoaded ? ( ) : null} { setImageLoaded(true) diff --git a/src/components/Parse/Emojis.tsx b/src/components/Parse/Emojis.tsx index 0c23a5f7..91a5ad40 100644 --- a/src/components/Parse/Emojis.tsx +++ b/src/components/Parse/Emojis.tsx @@ -1,5 +1,6 @@ import CustomText from '@components/Text' import { useAccessibility } from '@utils/accessibility/AccessibilityManager' +import { connectImage } from '@utils/api/helpers/connect' import { useGlobalStorage } from '@utils/storage/actions' import { StyleConstants } from '@utils/styles/constants' import { adaptiveScale } from '@utils/styles/scaling' @@ -75,7 +76,7 @@ const ParseEmojis: React.FC = ({ {i === 0 ? ' ' : undefined} > {reaction.url ? ( ))} diff --git a/src/screens/Compose/Root/Footer/Attachments.tsx b/src/screens/Compose/Root/Footer/Attachments.tsx index 0853d5e3..3aae6988 100644 --- a/src/screens/Compose/Root/Footer/Attachments.tsx +++ b/src/screens/Compose/Root/Footer/Attachments.tsx @@ -6,6 +6,7 @@ import { MAX_MEDIA_ATTACHMENTS } from '@components/mediaSelector' import CustomText from '@components/Text' import { useActionSheet } from '@expo/react-native-action-sheet' import { useNavigation } from '@react-navigation/native' +import { connectImage } from '@utils/api/helpers/connect' import { featureCheck } from '@utils/helpers/featureCheck' import { StyleConstants } from '@utils/styles/constants' import layoutAnimation from '@utils/styles/layoutAnimation' @@ -105,7 +106,11 @@ const ComposeAttachments: React.FC = ({ accessibleRefAttachments }) => { > {item.remote?.meta?.original?.duration ? ( { const navigation = useNavigation() @@ -24,6 +25,22 @@ const SettingsApp: React.FC = () => { const [browser, setBrowser] = useGlobalStorage.string('app.browser') const [autoplayGifv, setAutoplayGifv] = useGlobalStorage.boolean('app.auto_play_gifv') + const [connect, setConnect] = useGlobalStorage.boolean('app.connect') + const [showConnect, setShowConnect] = useState(connect) + useEffect(() => { + connectVerify() + .then(() => { + setShowConnect(true) + }) + .catch(() => { + if (connect) { + setConnect(false) + } else { + setShowConnect(false) + } + }) + }, []) + return ( { switchValue={autoplayGifv} switchOnValueChange={() => setAutoplayGifv(!autoplayGifv)} /> + {showConnect ? ( + setConnect(!connect)} + /> + ) : null} ) } diff --git a/src/utils/api/general.ts b/src/utils/api/general.ts index b1ef9780..7f72a091 100644 --- a/src/utils/api/general.ts +++ b/src/utils/api/general.ts @@ -1,5 +1,7 @@ +import { getGlobalStorage } from '@utils/storage/actions' import axios from 'axios' import { ctx, handleError, PagedResponse, parseHeaderLinks, userAgent } from './helpers' +import { CONNECT_DOMAIN } from './helpers/connect' export type Params = { method: 'get' | 'post' | 'put' | 'delete' @@ -32,17 +34,20 @@ const apiGeneral = async ({ params ? params : '' ) + const useConnect = getGlobalStorage.boolean('app.connect') + return axios({ timeout: method === 'post' ? 1000 * 60 : 1000 * 15, method, - baseURL: `https://${domain}/`, + baseURL: `https://${useConnect ? CONNECT_DOMAIN() : domain}`, url, params, headers: { Accept: 'application/json', ...userAgent, ...headers, - ...(body && body instanceof FormData && { 'Content-Type': 'multipart/form-data' }) + ...(body && body instanceof FormData && { 'Content-Type': 'multipart/form-data' }), + ...(useConnect && { 'x-tooot-domain': domain }) }, data: body }) diff --git a/src/utils/api/helpers/connect.ts b/src/utils/api/helpers/connect.ts new file mode 100644 index 00000000..327fa380 --- /dev/null +++ b/src/utils/api/helpers/connect.ts @@ -0,0 +1,144 @@ +import { mapEnvironment } from '@utils/helpers/checkEnvironment' +import { getGlobalStorage, setGlobalStorage } from '@utils/storage/actions' +import axios from 'axios' +import parse from 'url-parse' +import { userAgent } from '.' + +const list = [ + 'n61owz4leck', + 'z9skyp2f0m', + 'nc2dqtyxevj', + 'tgl97fgudrf', + 'eo2sj0ut2s', + 'a75auwihvyi', + 'vzkpud5y5b', + '3uivf7yyex', + 'pxfoa1wbor', + '3cor5jempc', + '9o32znuepr', + '9ayt1l2dzpi', + '60iu4rz8js', + 'dzoa1lbxbv', + '82rpiiqw21', + 'fblij1c9gyl', + 'wk2x048g8gl', + '9x91yrbtmn', + 'dgu5p7eif6', + 'uftwyhrkgrh', + 'vv5hay15vjk', + 'ooj9ihtyur', + 'o8r7phzd58', + 'pujwyg269s', + 'l6yq5nr8lv', + 'ocyrlfmdnl', + 'rdtpeip5e2', + 'ykzb5784js', + 'm34z7j5us1i', + 'tqsfr0orqa', + '8ncrt0mifa', + 'ygce2fdmsm', + '22vk7csljz', + '7mmb6hrih1', + 'grla5cpgau', + '0vygyvs4k7', + '1texbe32sf', + 'ckwvauiiol', + 'qkxryrbpxx', + 'ptb19c0ks9g', + '3bpe76o6stg', + 'd507ejce9g', + 'jpul5v2mqej', + '6m5uxemc79', + 'wxbtoo9t3p', + '8qco3d0idh', + 'u00c2xiabvf', + 'hutkqwrcy8', + 't6vrkzhpzo', + 'wy6e529mnb', + 'kzzrlfa59pg', + 'mmo4sv4a7s', + 'u0dishl20k', + '8qyx25bq3u', + 'd3mucdzlu1', + 'y123m81vsjl', + '51opvzdo6k', + 'r4z333th9u', + 'q77hl0ggfr', + 'bsk1f2wi52g', + 'eubnxpv0pz', + 'h11pk7qm8i', + 'brhxw45vd5', + 'vtnvlsrn1z', + '0q5w0hhzb5', + 'vq2rz02ayf', + 'hml3igfwkq', + '39qs7vhenl', + '5vcv775rug', + 'kjom5gr7i3', + 't2kmaoeb5x', + 'ni6ow1z11b', + 'yvgtoc3d88', + 'iax04eatnz', + 'esxyu9zujg', + '73xa28n278', + '5x63a8l24k', + 'dy1trb0b3sj', + 'd4c31j23m8', + 'ho76046l0j', + 'sw8lj5u2ef', + 'z5cn21mew5', + 'wxj73nmqwa', + 'gdj00dlx98', + '0v76xag64i', + 'j35104qduhj', + 'l63r7h0ss6', + 'e5xdv7t1q0h', + '4icoh8t4c8', + 'nbk36jt4sq', + 'zi0n0cv4tk', + 'o7qkfp3rxu', + 'xd2wefzd27', + 'rg7e6tsacx', + '9lrq3s4vfm', + 'srs9p21lxoh', + 'n8xymau42t', + 'q5cik283fg', + '68ye9feqs5', + 'xjc5anubnv' +] + +export const CONNECT_DOMAIN = () => + mapEnvironment({ + release: `${list[Math.floor(Math.random() * (100 - 0) + 0)]}.tooot.app`, + candidate: 'connect-candidate.tooot.app', + development: 'connect-development.tooot.app' + }) + +export const connectImage = ({ + uri +}: { + uri?: string +}): { uri?: string; headers?: { 'x-tooot-domain': string } } => { + const connect = getGlobalStorage.boolean('app.connect') + if (connect) { + if (uri) { + const host = parse(uri).host + return { uri: uri.replace(host, CONNECT_DOMAIN()), headers: { 'x-tooot-domain': host } } + } else { + return { uri } + } + } else { + return { uri } + } +} + +export const connectVerify = () => + axios({ + method: 'get', + baseURL: `https://${CONNECT_DOMAIN()}`, + url: 'verify', + headers: { ...userAgent } + }).catch(err => { + setGlobalStorage('app.connect', false) + return Promise.reject(err) + }) diff --git a/src/utils/api/instance.ts b/src/utils/api/instance.ts index ea435c4d..71568650 100644 --- a/src/utils/api/instance.ts +++ b/src/utils/api/instance.ts @@ -1,7 +1,8 @@ -import { getAccountDetails } from '@utils/storage/actions' +import { getAccountDetails, getGlobalStorage } from '@utils/storage/actions' import { StorageGlobal } from '@utils/storage/global' import axios, { AxiosRequestConfig } from 'axios' import { ctx, handleError, PagedResponse, parseHeaderLinks, userAgent } from './helpers' +import { CONNECT_DOMAIN } from './helpers/connect' export type Params = { account?: StorageGlobal['account.active'] @@ -43,11 +44,15 @@ const apiInstance = async ({ method + ctx.blue(' -> ') + `/${url}` + (params ? ctx.blue(' -> ') : ''), params ? params : '' ) - console.log('body', body) + + const useConnect = getGlobalStorage.boolean('app.connect') + return axios({ timeout: method === 'post' ? 1000 * 60 : 1000 * 15, method, - baseURL: `https://${accountDetails['auth.domain']}/api/${version}/`, + baseURL: `https://${ + useConnect ? CONNECT_DOMAIN() : accountDetails['auth.domain'] + }/api/${version}`, url, params, headers: { @@ -55,7 +60,8 @@ const apiInstance = async ({ ...userAgent, ...headers, Authorization: `Bearer ${accountDetails['auth.token']}`, - ...(body && body instanceof FormData && { 'Content-Type': 'multipart/form-data' }) + ...(body && body instanceof FormData && { 'Content-Type': 'multipart/form-data' }), + ...(useConnect && { 'x-tooot-domain': accountDetails['auth.domain'] }) }, data: body, ...extras diff --git a/src/utils/storage/global/v0.ts b/src/utils/storage/global/v0.ts index 8554c084..4acdfc39 100644 --- a/src/utils/storage/global/v0.ts +++ b/src/utils/storage/global/v0.ts @@ -17,6 +17,7 @@ export type GlobalV0 = { 'version.account': number // boolean 'app.auto_play_gifv'?: boolean + 'app.connect'?: boolean //// account // string