tooot/src/components/Instance.tsx

354 lines
11 KiB
TypeScript
Raw Normal View History

2021-01-07 19:13:09 +01:00
import Button from '@components/Button'
import haptics from '@components/haptics'
import Icon from '@components/Icon'
import { useNavigation } from '@react-navigation/native'
2021-01-11 21:36:57 +01:00
import { useAppsQuery } from '@utils/queryHooks/apps'
import { useInstanceQuery } from '@utils/queryHooks/instance'
2021-01-07 19:13:09 +01:00
import { QueryKeyTimeline } from '@utils/queryHooks/timeline'
2021-01-20 12:35:24 +01:00
import { getLocalInstances, remoteUpdate } from '@utils/slices/instancesSlice'
2021-01-07 19:13:09 +01:00
import { StyleConstants } from '@utils/styles/constants'
import { useTheme } from '@utils/styles/ThemeManager'
2021-01-19 01:13:45 +01:00
import * as Linking from 'expo-linking'
2021-01-07 19:13:09 +01:00
import { debounce } from 'lodash'
2021-01-22 01:34:20 +01:00
import React, { useCallback, useMemo, useState } from 'react'
2021-01-07 19:13:09 +01:00
import { useTranslation } from 'react-i18next'
2021-01-10 02:12:14 +01:00
import { Alert, Image, StyleSheet, Text, TextInput, View } from 'react-native'
2021-01-07 19:13:09 +01:00
import { useQueryClient } from 'react-query'
2021-01-10 02:12:14 +01:00
import { useDispatch, useSelector } from 'react-redux'
2021-01-23 02:41:50 +01:00
import { Placeholder, Fade } from 'rn-placeholder'
2021-01-24 02:25:43 +01:00
import analytics from './analytics'
2021-01-07 19:13:09 +01:00
import InstanceAuth from './Instance/Auth'
import InstanceInfo from './Instance/Info'
import { toast } from './toast'
export interface Props {
type: 'local' | 'remote'
disableHeaderImage?: boolean
goBack?: boolean
}
const ComponentInstance: React.FC<Props> = ({
type,
disableHeaderImage,
goBack = false
}) => {
2021-01-19 01:13:45 +01:00
const { t } = useTranslation('componentInstance')
2021-01-07 19:13:09 +01:00
const { theme } = useTheme()
2021-01-20 12:35:24 +01:00
const navigation = useNavigation()
2021-01-10 02:12:14 +01:00
const localInstances = useSelector(getLocalInstances)
2021-01-20 12:35:24 +01:00
const dispatch = useDispatch()
const queryClient = useQueryClient()
const [instanceDomain, setInstanceDomain] = useState<string>()
2021-01-07 19:13:09 +01:00
2021-01-11 21:36:57 +01:00
const instanceQuery = useInstanceQuery({
2021-01-07 19:13:09 +01:00
instanceDomain,
2021-01-22 01:34:20 +01:00
checkPublic: type === 'remote',
2021-01-07 19:13:09 +01:00
options: { enabled: false, retry: false }
})
2021-01-20 12:35:24 +01:00
const appsQuery = useAppsQuery({
2021-01-07 19:13:09 +01:00
instanceDomain,
options: { enabled: false, retry: false }
})
const onChangeText = useCallback(
debounce(
text => {
setInstanceDomain(text.replace(/^http(s)?\:\/\//i, ''))
2021-01-20 12:35:24 +01:00
appsQuery.remove()
if (text) {
instanceQuery.refetch()
}
2021-01-07 19:13:09 +01:00
},
1000,
2021-01-20 12:35:24 +01:00
{ trailing: true }
2021-01-07 19:13:09 +01:00
),
[]
)
const processUpdate = useCallback(() => {
if (instanceDomain) {
switch (type) {
case 'local':
2021-01-24 02:25:43 +01:00
analytics('instance_local_login')
2021-01-10 02:12:14 +01:00
if (
localInstances &&
localInstances.filter(instance => instance.url === instanceDomain)
.length
) {
Alert.alert(
2021-01-19 01:13:45 +01:00
t('update.local.alert.title'),
t('update.local.alert.message'),
2021-01-10 02:12:14 +01:00
[
2021-01-20 00:39:39 +01:00
{
text: t('update.local.alert.buttons.cancel'),
style: 'cancel'
},
2021-01-10 02:12:14 +01:00
{
2021-01-19 01:13:45 +01:00
text: t('update.local.alert.buttons.continue'),
2021-01-10 02:12:14 +01:00
onPress: () => {
2021-01-20 12:35:24 +01:00
appsQuery.refetch()
2021-01-10 02:12:14 +01:00
}
}
]
)
} else {
2021-01-20 12:35:24 +01:00
appsQuery.refetch()
2021-01-10 02:12:14 +01:00
}
break
2021-01-07 19:13:09 +01:00
case 'remote':
2021-01-24 02:25:43 +01:00
analytics('instance_remote_register')
2021-01-10 02:12:14 +01:00
haptics('Success')
2021-01-07 19:13:09 +01:00
const queryKey: QueryKeyTimeline = [
'Timeline',
{ page: 'RemotePublic' }
]
dispatch(remoteUpdate(instanceDomain))
queryClient.resetQueries(queryKey)
2021-01-19 01:13:45 +01:00
toast({ type: 'success', message: t('update.remote.succeed') })
navigation.goBack()
2021-01-10 02:12:14 +01:00
break
2021-01-07 19:13:09 +01:00
}
}
}, [instanceDomain])
const onSubmitEditing = useCallback(
({ nativeEvent: { text } }) => {
2021-01-24 02:25:43 +01:00
analytics('instance_textinput_submit', { match: text === instanceDomain })
2021-01-07 19:13:09 +01:00
if (
text === instanceDomain &&
instanceQuery.isSuccess &&
instanceQuery.data &&
instanceQuery.data.uri
) {
processUpdate()
}
},
[instanceDomain, instanceQuery.isSuccess, instanceQuery.data]
)
const buttonContent = useMemo(() => {
switch (type) {
case 'local':
2021-01-19 01:13:45 +01:00
return t('server.button.local')
2021-01-07 19:13:09 +01:00
case 'remote':
2021-01-19 01:13:45 +01:00
return t('server.button.remote')
2021-01-07 19:13:09 +01:00
}
}, [])
2021-01-20 12:35:24 +01:00
const requestAuth = useMemo(() => {
if (
instanceDomain &&
instanceQuery.data?.uri &&
appsQuery.data?.client_id &&
appsQuery.data.client_secret
) {
return (
<InstanceAuth
key={Math.random()}
instanceDomain={instanceDomain}
instanceUri={instanceQuery.data.uri}
appData={{
clientId: appsQuery.data.client_id,
clientSecret: appsQuery.data.client_secret
}}
goBack={goBack}
/>
)
}
}, [instanceDomain, instanceQuery.data, appsQuery.data])
2021-01-07 19:13:09 +01:00
return (
<>
{!disableHeaderImage ? (
<View style={styles.imageContainer}>
<Image
source={require('assets/screens/meRoot/welcome.png')}
style={styles.image}
/>
</View>
) : null}
<View style={styles.base}>
<View style={styles.inputRow}>
<TextInput
style={[
styles.textInput,
{
color: theme.primary,
2021-01-22 01:34:20 +01:00
borderBottomColor:
type === 'remote' &&
instanceQuery.data &&
!instanceQuery.data.publicAllow
? theme.red
: theme.border
2021-01-07 19:13:09 +01:00
}
]}
onChangeText={onChangeText}
autoCapitalize='none'
autoCorrect={false}
clearButtonMode='never'
keyboardType='url'
textContentType='URL'
onSubmitEditing={onSubmitEditing}
2021-01-19 01:13:45 +01:00
placeholder={t('server.textInput.placeholder')}
2021-01-07 19:13:09 +01:00
placeholderTextColor={theme.secondary}
returnKeyType='go'
/>
<Button
type='text'
content={buttonContent}
onPress={processUpdate}
2021-01-22 01:34:20 +01:00
disabled={
!instanceQuery.data?.uri ||
(type === 'remote' && !instanceQuery.data.publicAllow)
}
2021-01-20 12:35:24 +01:00
loading={instanceQuery.isFetching || appsQuery.isFetching}
2021-01-07 19:13:09 +01:00
/>
</View>
2021-01-22 01:34:20 +01:00
{type === 'remote' &&
instanceQuery.data &&
!instanceQuery.data.publicAllow ? (
<Text style={[styles.privateInstance, { color: theme.red }]}>
{t('server.privateInstance')}
</Text>
) : null}
2021-01-07 19:13:09 +01:00
<View>
2021-01-23 02:41:50 +01:00
<Placeholder
{...(instanceQuery.isFetching && {
Animation: props => (
<Fade
{...props}
style={{ backgroundColor: theme.shimmerHighlight }}
/>
)
})}
>
2021-01-07 19:13:09 +01:00
<InstanceInfo
2021-01-23 02:41:50 +01:00
visible={instanceQuery.data?.title !== undefined}
header={t('server.information.name')}
content={instanceQuery.data?.title || undefined}
potentialWidth={2}
2021-01-07 19:13:09 +01:00
/>
<InstanceInfo
2021-01-23 02:41:50 +01:00
visible={instanceQuery.data?.short_description !== undefined}
header={t('server.information.description.heading')}
content={instanceQuery.data?.short_description || undefined}
potentialLines={5}
2021-01-07 19:13:09 +01:00
/>
2021-01-23 02:41:50 +01:00
<View style={styles.instanceStats}>
<InstanceInfo
style={styles.stat1}
visible={instanceQuery.data?.stats?.user_count !== undefined}
header={t('server.information.accounts')}
content={
instanceQuery.data?.stats?.user_count?.toString() || undefined
}
potentialWidth={4}
/>
<InstanceInfo
style={styles.stat2}
visible={instanceQuery.data?.stats?.status_count !== undefined}
header={t('server.information.statuses')}
content={
instanceQuery.data?.stats?.status_count?.toString() ||
undefined
}
potentialWidth={4}
/>
<InstanceInfo
style={styles.stat3}
visible={instanceQuery.data?.stats?.domain_count !== undefined}
header={t('server.information.domains')}
content={
instanceQuery.data?.stats?.domain_count?.toString() ||
undefined
}
potentialWidth={4}
/>
</View>
</Placeholder>
2021-01-20 00:39:39 +01:00
{type === 'local' ? (
<View style={styles.disclaimer}>
<Icon
name='Lock'
size={StyleConstants.Font.Size.S}
color={theme.secondary}
style={styles.disclaimerIcon}
/>
2021-01-23 02:41:50 +01:00
<Text style={[styles.disclaimerText, { color: theme.secondary }]}>
2021-01-20 00:39:39 +01:00
{t('server.disclaimer')}
2021-01-23 02:41:50 +01:00
<Text
style={{ color: theme.blue }}
2021-01-24 02:25:43 +01:00
onPress={() => {
analytics('view_privacy')
Linking.openURL('https://tooot.app/privacy')
}}
2021-01-23 02:41:50 +01:00
>
2021-01-20 00:39:39 +01:00
https://tooot.app/privacy
</Text>
2021-01-19 01:13:45 +01:00
</Text>
2021-01-20 00:39:39 +01:00
</View>
) : null}
2021-01-07 19:13:09 +01:00
</View>
</View>
2021-01-20 12:35:24 +01:00
{type === 'local' ? requestAuth : null}
2021-01-07 19:13:09 +01:00
</>
)
}
const styles = StyleSheet.create({
imageContainer: { flexDirection: 'row' },
image: { resizeMode: 'contain', flex: 1, aspectRatio: 16 / 9 },
base: {
marginVertical: StyleConstants.Spacing.L,
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
},
inputRow: {
flexDirection: 'row',
marginHorizontal: StyleConstants.Spacing.Global.PagePadding
},
textInput: {
flex: 1,
borderBottomWidth: 1,
...StyleConstants.FontStyle.M,
marginRight: StyleConstants.Spacing.M
},
2021-01-22 01:34:20 +01:00
privateInstance: {
...StyleConstants.FontStyle.S,
fontWeight: StyleConstants.Font.Weight.Bold,
marginLeft: StyleConstants.Spacing.Global.PagePadding,
marginTop: StyleConstants.Spacing.XS
},
2021-01-07 19:13:09 +01:00
instanceStats: {
flex: 1,
flexDirection: 'row'
},
2021-01-20 12:35:24 +01:00
stat1: {
alignItems: 'flex-start'
},
stat2: {
alignItems: 'center'
},
stat3: {
alignItems: 'flex-end'
},
2021-01-07 19:13:09 +01:00
disclaimer: {
2021-01-19 01:13:45 +01:00
flexDirection: 'row',
2021-01-07 19:13:09 +01:00
marginHorizontal: StyleConstants.Spacing.Global.PagePadding,
marginVertical: StyleConstants.Spacing.M
2021-01-19 01:13:45 +01:00
},
disclaimerIcon: {
marginTop:
(StyleConstants.Font.LineHeight.S - StyleConstants.Font.Size.S) / 2,
marginRight: StyleConstants.Spacing.XS
},
disclaimerText: {
flex: 1,
...StyleConstants.FontStyle.S
2021-01-07 19:13:09 +01:00
}
})
export default ComponentInstance