mirror of
https://github.com/tooot-app/app
synced 2025-04-17 03:37:21 +02:00
parent
74e794a215
commit
196f51bfca
34
src/App.tsx
34
src/App.tsx
@ -10,13 +10,7 @@ import log from '@utils/startup/log'
|
||||
import netInfo from '@utils/startup/netInfo'
|
||||
import push from '@utils/startup/push'
|
||||
import sentry from '@utils/startup/sentry'
|
||||
import { storage } from '@utils/storage'
|
||||
import {
|
||||
getGlobalStorage,
|
||||
removeAccount,
|
||||
setAccount,
|
||||
setGlobalStorage
|
||||
} from '@utils/storage/actions'
|
||||
import { getGlobalStorage, setAccount, setGlobalStorage } from '@utils/storage/actions'
|
||||
import { migrateFromAsyncStorage, versionStorageGlobal } from '@utils/storage/migrations/toMMKV'
|
||||
import ThemeManager from '@utils/styles/ThemeManager'
|
||||
import * as Localization from 'expo-localization'
|
||||
@ -24,7 +18,6 @@ import * as SplashScreen from 'expo-splash-screen'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { LogBox, Platform } from 'react-native'
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
||||
import { MMKV } from 'react-native-mmkv'
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context'
|
||||
import { enableFreeze } from 'react-native-screens'
|
||||
import i18n from './i18n'
|
||||
@ -36,6 +29,7 @@ Platform.select({
|
||||
|
||||
dev()
|
||||
sentry()
|
||||
netInfo()
|
||||
audio()
|
||||
push()
|
||||
enableFreeze(true)
|
||||
@ -46,7 +40,6 @@ SplashScreen.preventAutoHideAsync()
|
||||
const App: React.FC = () => {
|
||||
log('log', 'App', 'rendering App')
|
||||
const [appIsReady, setAppIsReady] = useState(false)
|
||||
const [localCorrupt, setLocalCorrupt] = useState<string>()
|
||||
|
||||
const [hasMigrated, setHasMigrated] = useState<boolean>(versionStorageGlobal !== undefined)
|
||||
|
||||
@ -61,36 +54,19 @@ const App: React.FC = () => {
|
||||
log('log', 'App', 'loading from MMKV')
|
||||
const account = getGlobalStorage.string('account.active')
|
||||
if (account) {
|
||||
const storageAccount = new MMKV({ id: account })
|
||||
const token = storageAccount.getString('auth.token')
|
||||
if (token) {
|
||||
log('log', 'App', `Binding storage of ${account}`)
|
||||
storage.account = storageAccount
|
||||
} else {
|
||||
log('log', 'App', `Token not found for ${account}`)
|
||||
removeAccount(account)
|
||||
}
|
||||
await setAccount(account)
|
||||
} 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]}`)
|
||||
setAccount(accounts[accounts.length - 1])
|
||||
await setAccount(accounts[accounts.length - 1])
|
||||
} else {
|
||||
setGlobalStorage('account.active', undefined)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let netInfoRes = undefined
|
||||
try {
|
||||
netInfoRes = await netInfo()
|
||||
} catch {}
|
||||
|
||||
if (netInfoRes && netInfoRes.corrupted && netInfoRes.corrupted.length) {
|
||||
setLocalCorrupt(netInfoRes.corrupted)
|
||||
}
|
||||
|
||||
log('log', 'App', `locale: ${Localization.locale}`)
|
||||
const language = getLanguage()
|
||||
if (!language) {
|
||||
@ -124,7 +100,7 @@ const App: React.FC = () => {
|
||||
<ActionSheetProvider>
|
||||
<AccessibilityManager>
|
||||
<ThemeManager>
|
||||
<Screens localCorrupt={localCorrupt} />
|
||||
<Screens />
|
||||
</ThemeManager>
|
||||
</AccessibilityManager>
|
||||
</ActionSheetProvider>
|
||||
|
@ -193,10 +193,12 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
}
|
||||
})
|
||||
|
||||
const scopes = featureCheck('deprecate_auth_follow')
|
||||
? ['read', 'write', 'push']
|
||||
: ['read', 'write', 'follow', 'push']
|
||||
const processUpdate = useCallback(() => {
|
||||
const scopes = () =>
|
||||
featureCheck('deprecate_auth_follow', instanceQuery.data?.version)
|
||||
? ['read', 'write', 'push']
|
||||
: ['read', 'write', 'follow', 'push']
|
||||
|
||||
if (domain) {
|
||||
const accounts = getGlobalStorage.object('accounts')
|
||||
if (accounts?.filter(account => account.startsWith(`${domain}/`)).length) {
|
||||
@ -210,15 +212,15 @@ const ComponentInstance: React.FC<Props> = ({
|
||||
},
|
||||
{
|
||||
text: t('common:buttons.continue'),
|
||||
onPress: () => appsMutation.mutate({ domain, scopes })
|
||||
onPress: () => appsMutation.mutate({ domain, scopes: scopes() })
|
||||
}
|
||||
]
|
||||
)
|
||||
} else {
|
||||
appsMutation.mutate({ domain, scopes })
|
||||
appsMutation.mutate({ domain, scopes: scopes() })
|
||||
}
|
||||
}
|
||||
}, [domain])
|
||||
}, [domain, instanceQuery.data?.version])
|
||||
|
||||
return (
|
||||
<KeyboardAvoidingView
|
||||
|
@ -32,11 +32,7 @@ import { routingInstrumentation } from '../utils/startup/sentry'
|
||||
|
||||
const Stack = createNativeStackNavigator<RootStackParamList>()
|
||||
|
||||
export interface Props {
|
||||
localCorrupt?: string
|
||||
}
|
||||
|
||||
const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
||||
const Screens: React.FC = () => {
|
||||
const { t, i18n } = useTranslation([
|
||||
'common',
|
||||
'screens',
|
||||
@ -64,24 +60,6 @@ const Screens: React.FC<Props> = ({ localCorrupt }) => {
|
||||
return () => screenshotListener.remove()
|
||||
}, [])
|
||||
|
||||
// On launch display login credentials corrupt information
|
||||
useEffect(() => {
|
||||
const showLocalCorrect = () => {
|
||||
if (localCorrupt) {
|
||||
displayMessage({
|
||||
message: t('screens:localCorrupt.message'),
|
||||
description: localCorrupt.length ? localCorrupt : undefined,
|
||||
type: 'danger'
|
||||
})
|
||||
// @ts-ignore
|
||||
navigationRef.navigate('Screen-Tabs', {
|
||||
screen: 'Tab-Me'
|
||||
})
|
||||
}
|
||||
}
|
||||
return showLocalCorrect()
|
||||
}, [localCorrupt])
|
||||
|
||||
// Lazily update users's preferences, for e.g. composing default visibility
|
||||
useInstanceQuery({ options: { enabled: !!accountActive } })
|
||||
useProfileQuery({ options: { enabled: !!accountActive } })
|
||||
|
@ -51,8 +51,6 @@ const features = [
|
||||
}
|
||||
]
|
||||
|
||||
export const featureCheck = (feature: string): boolean => {
|
||||
const version = getAccountStorage.string('version')
|
||||
return !!features.filter(f => f.feature === feature).filter(f => parseFloat(version) >= f.version)
|
||||
?.length
|
||||
}
|
||||
export const featureCheck = (feature: string, v?: string): boolean =>
|
||||
(features.find(f => f.feature === feature)?.version || 999) <=
|
||||
parseFloat(v || getAccountStorage.string('version'))
|
||||
|
@ -1,67 +1,15 @@
|
||||
import NetInfo from '@react-native-community/netinfo'
|
||||
import { onlineManager } from '@tanstack/react-query'
|
||||
import apiInstance from '@utils/api/instance'
|
||||
import { storage } from '@utils/storage'
|
||||
import { getAccountStorage, removeAccount, setAccountStorage } from '@utils/storage/actions'
|
||||
import log from './log'
|
||||
|
||||
const netInfo = async (): Promise<{
|
||||
connected?: boolean
|
||||
corrupted?: string
|
||||
} | void> => {
|
||||
const netInfo = () => {
|
||||
log('log', 'netInfo', 'initializing')
|
||||
|
||||
const netInfo = await NetInfo.fetch()
|
||||
|
||||
onlineManager.setEventListener(setOnline => {
|
||||
return NetInfo.addEventListener(state => {
|
||||
setOnline(!!state.isConnected)
|
||||
})
|
||||
})
|
||||
|
||||
if (netInfo.isConnected) {
|
||||
log('log', 'netInfo', 'network connected')
|
||||
if (storage.account) {
|
||||
const domain = getAccountStorage.string('auth.domain')
|
||||
const id = getAccountStorage.string('auth.account.id')
|
||||
const account = `${domain}/${id}`
|
||||
log('log', 'netInfo', 'checking locally stored credentials')
|
||||
|
||||
let resVerify: Mastodon.Account
|
||||
try {
|
||||
resVerify = await apiInstance<Mastodon.Account>({
|
||||
method: 'get',
|
||||
url: `accounts/verify_credentials`
|
||||
}).then(res => res.body)
|
||||
} catch (error: any) {
|
||||
log('error', 'netInfo', 'local credential check failed')
|
||||
if (error?.status && error.status == 401) {
|
||||
removeAccount(account)
|
||||
}
|
||||
return Promise.resolve({ corrupted: error.data?.error })
|
||||
}
|
||||
|
||||
log('log', 'netInfo', 'local credential check passed')
|
||||
if (resVerify.id !== id) {
|
||||
log('error', 'netInfo', 'local id does not match remote id')
|
||||
removeAccount(account)
|
||||
return Promise.resolve({ connected: true, corrupted: '' })
|
||||
} else {
|
||||
setAccountStorage([
|
||||
{ key: 'auth.account.acct', value: resVerify.acct },
|
||||
{ key: 'auth.account.avatar_static', value: resVerify.avatar_static }
|
||||
])
|
||||
|
||||
return Promise.resolve({ connected: true })
|
||||
}
|
||||
} else {
|
||||
log('log', 'netInfo', 'no local credential found')
|
||||
return Promise.resolve()
|
||||
}
|
||||
} else {
|
||||
log('warn', 'netInfo', 'network not connected')
|
||||
return Promise.resolve()
|
||||
}
|
||||
}
|
||||
|
||||
export default netInfo
|
||||
|
@ -1,4 +1,9 @@
|
||||
import { displayMessage } from '@components/Message'
|
||||
import i18n from '@i18n/index'
|
||||
import apiGeneral from '@utils/api/general'
|
||||
import navigationRef from '@utils/navigation/navigationRef'
|
||||
import { queryClient } from '@utils/queryHooks'
|
||||
import log from '@utils/startup/log'
|
||||
import { storage } from '@utils/storage'
|
||||
import { Platform } from 'react-native'
|
||||
import {
|
||||
@ -222,10 +227,72 @@ export const generateAccountKey = ({
|
||||
}) => `${domain}/${id}`
|
||||
|
||||
export const setAccount = async (account: string) => {
|
||||
storage.account = new MMKV({ id: account })
|
||||
setGlobalStorage('account.active', account)
|
||||
await queryClient.resetQueries()
|
||||
queryClient.clear()
|
||||
const temp = new MMKV({ id: account })
|
||||
const token = temp.getString('auth.token')
|
||||
const domain = temp.getString('auth.domain')
|
||||
|
||||
if (!token || !domain) {
|
||||
await removeAccount(account)
|
||||
return
|
||||
}
|
||||
|
||||
await apiGeneral<Mastodon.Account>({
|
||||
method: 'get',
|
||||
domain,
|
||||
url: 'api/v1/accounts/verify_credentials',
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
})
|
||||
.then(res => res.body)
|
||||
.then(async a => {
|
||||
temp.set('auth.account.acct', a.acct)
|
||||
temp.set('auth.account.avatar_static', a.avatar_static)
|
||||
|
||||
log('log', 'setAccount', `binding storage of ${account}`)
|
||||
await queryClient.resetQueries()
|
||||
queryClient.clear()
|
||||
|
||||
storage.account = temp
|
||||
setGlobalStorage('account.active', account)
|
||||
})
|
||||
.catch(async error => {
|
||||
if (error?.status && error.status == 401) {
|
||||
log('log', 'setAccount', `unauthorised ${account}`)
|
||||
await removeAccount(account)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const removeAccount = async (account: string) => {
|
||||
displayMessage({
|
||||
message: i18n.t('screens:localCorrupt.message'),
|
||||
type: 'danger'
|
||||
})
|
||||
// @ts-ignore
|
||||
navigationRef.navigate('Screen-Tabs', { screen: 'Tab-Me' })
|
||||
|
||||
const currAccounts: NonNullable<StorageGlobal['accounts']> =
|
||||
getGlobalStorage.object('accounts') || []
|
||||
const nextAccounts: NonNullable<StorageGlobal['accounts']> = currAccounts.filter(
|
||||
a => a !== account
|
||||
)
|
||||
|
||||
storage.global.set('accounts', JSON.stringify(nextAccounts))
|
||||
|
||||
if (nextAccounts.length) {
|
||||
log('log', 'removeAccount', `trying next account ${nextAccounts[nextAccounts.length - 1]}`)
|
||||
await setAccount(nextAccounts[nextAccounts.length - 1])
|
||||
} else {
|
||||
log('log', 'removeAccount', 'setting to undefined')
|
||||
await queryClient.resetQueries()
|
||||
queryClient.clear()
|
||||
|
||||
storage.account = undefined
|
||||
setGlobalStorage('account.active', undefined)
|
||||
}
|
||||
|
||||
new MMKV({ id: account }).clearAll()
|
||||
}
|
||||
|
||||
export type ReadableAccountType = {
|
||||
@ -263,25 +330,3 @@ export const getReadableAccounts = (withoutActive: boolean = false): ReadableAcc
|
||||
}) || []
|
||||
).filter(a => a.acct.length)
|
||||
}
|
||||
|
||||
export const removeAccount = async (account: string) => {
|
||||
const currAccounts: NonNullable<StorageGlobal['accounts']> = JSON.parse(
|
||||
storage.global.getString('accounts') || '[]'
|
||||
)
|
||||
const nextAccounts: NonNullable<StorageGlobal['accounts']> = currAccounts.filter(
|
||||
a => a !== account
|
||||
)
|
||||
|
||||
storage.global.set('accounts', JSON.stringify(nextAccounts))
|
||||
|
||||
if (nextAccounts.length) {
|
||||
await setAccount(nextAccounts[nextAccounts.length - 1])
|
||||
} else {
|
||||
storage.account = undefined
|
||||
setGlobalStorage('account.active', undefined)
|
||||
queryClient.clear()
|
||||
}
|
||||
|
||||
const temp = new MMKV({ id: account })
|
||||
temp.clearAll()
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user