mirror of https://github.com/tooot-app/app
Restyle login
This commit is contained in:
parent
48cab6053c
commit
dc9870beaf
4
App.tsx
4
App.tsx
|
@ -4,8 +4,8 @@ import { QueryCache, ReactQueryCacheProvider, setConsole } from 'react-query'
|
|||
import { Provider } from 'react-redux'
|
||||
import { PersistGate } from 'redux-persist/integration/react'
|
||||
|
||||
import { Index } from './src/Index'
|
||||
import { persistor, store } from './src/store'
|
||||
import { Index } from '@root/Index'
|
||||
import { persistor, store } from '@root/store'
|
||||
import ThemeManager from '@utils/styles/ThemeManager'
|
||||
|
||||
const queryCache = new QueryCache()
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 383 KiB |
|
@ -9,6 +9,7 @@ module.exports = function (api) {
|
|||
{
|
||||
root: ['./'],
|
||||
alias: {
|
||||
'@assets': './assets',
|
||||
'@root': './src',
|
||||
'@api': './src/api',
|
||||
'@components': './src/components',
|
||||
|
|
|
@ -226,6 +226,32 @@ declare namespace Mastodon {
|
|||
title: string
|
||||
}
|
||||
|
||||
type Instance = {
|
||||
// Base
|
||||
uri: string
|
||||
title: string
|
||||
description: string
|
||||
short_description: string
|
||||
email: string
|
||||
version: string
|
||||
languages: string[]
|
||||
registrations: boolean
|
||||
approval_required: boolean
|
||||
invites_enabled: boolean
|
||||
urls: {
|
||||
streaming_api: string
|
||||
}
|
||||
stats: {
|
||||
user_count: number
|
||||
status_count: number
|
||||
domain_count: number
|
||||
}
|
||||
|
||||
// Others
|
||||
thumbnail?: string
|
||||
contact_account?: Account
|
||||
}
|
||||
|
||||
type Mention = {
|
||||
// Base
|
||||
id: string
|
||||
|
|
|
@ -18,6 +18,8 @@ import { useTheme } from '@utils/styles/ThemeManager'
|
|||
import getCurrentTab from '@utils/getCurrentTab'
|
||||
import { toastConfig } from '@components/toast'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { getLocalUrl } from './utils/slices/instancesSlice'
|
||||
|
||||
enableScreens()
|
||||
const Tab = createBottomTabNavigator<RootStackParamList>()
|
||||
|
@ -31,6 +33,7 @@ export type RootStackParamList = {
|
|||
}
|
||||
|
||||
export const Index: React.FC = () => {
|
||||
const localInstance = useSelector(getLocalUrl)
|
||||
const { i18n } = useTranslation()
|
||||
const { mode, theme } = useTheme()
|
||||
enum barStyle {
|
||||
|
@ -46,12 +49,14 @@ export const Index: React.FC = () => {
|
|||
screenOptions={({ route }) => ({
|
||||
tabBarIcon: ({ focused, color, size }) => {
|
||||
let name: any
|
||||
let updateColor: string = color
|
||||
switch (route.name) {
|
||||
case 'Screen-Local':
|
||||
name = 'home'
|
||||
break
|
||||
case 'Screen-Public':
|
||||
name = 'globe'
|
||||
!focused && (updateColor = theme.secondary)
|
||||
break
|
||||
case 'Screen-Post':
|
||||
name = 'plus'
|
||||
|
@ -61,27 +66,39 @@ export const Index: React.FC = () => {
|
|||
break
|
||||
case 'Screen-Me':
|
||||
name = focused ? 'meh' : 'smile'
|
||||
!focused && (updateColor = theme.secondary)
|
||||
break
|
||||
default:
|
||||
name = 'alert-octagon'
|
||||
break
|
||||
}
|
||||
return <Feather name={name} size={size} color={color} />
|
||||
return <Feather name={name} size={size} color={updateColor} />
|
||||
}
|
||||
})}
|
||||
tabBarOptions={{
|
||||
activeTintColor: theme.primary,
|
||||
inactiveTintColor: theme.secondary,
|
||||
inactiveTintColor: localInstance ? theme.secondary : theme.disabled,
|
||||
showLabel: false
|
||||
}}
|
||||
>
|
||||
<Tab.Screen name='Screen-Local' component={ScreenLocal} />
|
||||
<Tab.Screen
|
||||
name='Screen-Local'
|
||||
component={ScreenLocal}
|
||||
listeners={{
|
||||
tabPress: e => {
|
||||
if (!localInstance) {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen name='Screen-Public' component={ScreenPublic} />
|
||||
<Tab.Screen
|
||||
name='Screen-Post'
|
||||
listeners={({ navigation, route }) => ({
|
||||
tabPress: e => {
|
||||
e.preventDefault()
|
||||
localInstance &&
|
||||
navigation.navigate(getCurrentTab(navigation), {
|
||||
screen: 'Screen-Shared-Compose'
|
||||
})
|
||||
|
@ -93,6 +110,13 @@ export const Index: React.FC = () => {
|
|||
<Tab.Screen
|
||||
name='Screen-Notifications'
|
||||
component={ScreenNotifications}
|
||||
listeners={{
|
||||
tabPress: e => {
|
||||
if (!localInstance) {
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<Tab.Screen name='Screen-Me' component={ScreenMe} />
|
||||
</Tab.Navigator>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import axios from 'axios'
|
||||
import { store, RootState } from '@root/store'
|
||||
import { store, RootState } from 'src/store'
|
||||
|
||||
const client = async ({
|
||||
method,
|
||||
|
|
|
@ -8,18 +8,17 @@ type PropsBase = {
|
|||
onPress: () => void
|
||||
disabled?: boolean
|
||||
buttonSize?: 'S' | 'M'
|
||||
size?: 'S' | 'M' | 'L'
|
||||
}
|
||||
|
||||
export interface PropsText extends PropsBase {
|
||||
text: string
|
||||
icon?: any
|
||||
size?: 'S' | 'M' | 'L'
|
||||
}
|
||||
|
||||
export interface PropsIcon extends PropsBase {
|
||||
text?: string
|
||||
icon: any
|
||||
size?: 'S' | 'M' | 'L'
|
||||
}
|
||||
|
||||
const ButtonRow: React.FC<PropsText | PropsIcon> = ({
|
||||
|
@ -71,16 +70,13 @@ const styles = StyleSheet.create({
|
|||
button: {
|
||||
paddingLeft: StyleConstants.Spacing.M,
|
||||
paddingRight: StyleConstants.Spacing.M,
|
||||
borderWidth: 1,
|
||||
borderRadius: 100
|
||||
borderWidth: 1.25,
|
||||
borderRadius: 100,
|
||||
alignItems: 'center'
|
||||
},
|
||||
text: {
|
||||
textAlign: 'center'
|
||||
}
|
||||
})
|
||||
|
||||
export default React.memo(ButtonRow, (prev, next) => {
|
||||
let skipUpdate = true
|
||||
skipUpdate = prev.disabled === next.disabled
|
||||
return skipUpdate
|
||||
})
|
||||
export default ButtonRow
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
import React from 'react'
|
||||
import { Text } from 'react-native'
|
||||
|
||||
const PleaseLogin = () => {
|
||||
return <Text>请先登录</Text>
|
||||
}
|
||||
|
||||
export default PleaseLogin
|
|
@ -15,7 +15,6 @@ import {
|
|||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import getCurrentTab from '@utils/getCurrentTab'
|
||||
import PleaseLogin from '@components/PleaseLogin'
|
||||
|
||||
const Stack = createNativeStackNavigator()
|
||||
|
||||
|
@ -30,9 +29,7 @@ const Page = ({
|
|||
<View style={{ width: Dimensions.get('window').width }}>
|
||||
{localRegistered || page === 'RemotePublic' ? (
|
||||
<Timeline page={page} />
|
||||
) : (
|
||||
<PleaseLogin />
|
||||
)}
|
||||
) : null}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -28,13 +28,13 @@ const ScreenMe: React.FC = () => {
|
|||
name='Screen-Me-Root'
|
||||
component={ScreenMeRoot}
|
||||
options={
|
||||
localRegistered
|
||||
? {
|
||||
// localRegistered ?
|
||||
{
|
||||
headerTranslucent: true,
|
||||
headerStyle: { backgroundColor: 'rgba(255, 255, 255, 0)' },
|
||||
headerCenter: () => <></>
|
||||
}
|
||||
: { headerTitle: t('headers.me.root') }
|
||||
// : { headerTitle: t('meRoot:heading') }
|
||||
}
|
||||
/>
|
||||
<Stack.Screen
|
||||
|
|
|
@ -14,7 +14,7 @@ const ScreenMeRoot: React.FC = () => {
|
|||
const localRegistered = useSelector(getLocalUrl)
|
||||
|
||||
return (
|
||||
<ScrollView>
|
||||
<ScrollView keyboardShouldPersistTaps='handled'>
|
||||
{localRegistered ? <MyInfo /> : <Login />}
|
||||
{localRegistered && <Collections />}
|
||||
<Settings />
|
||||
|
|
|
@ -1,5 +1,19 @@
|
|||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { StyleSheet, Text, TextInput, View } from 'react-native'
|
||||
import React, {
|
||||
createRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState
|
||||
} from 'react'
|
||||
import {
|
||||
Animated,
|
||||
Dimensions,
|
||||
Image,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
View
|
||||
} from 'react-native'
|
||||
import { useQuery } from 'react-query'
|
||||
import { debounce } from 'lodash'
|
||||
|
||||
|
@ -14,6 +28,9 @@ import { useTheme } from '@utils/styles/ThemeManager'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { ButtonRow } from '@components/Button'
|
||||
import ParseContent from '@root/components/ParseContent'
|
||||
import ShimmerPlaceholder from 'react-native-shimmer-placeholder'
|
||||
import { Feather } from '@expo/vector-icons'
|
||||
|
||||
const Login: React.FC = () => {
|
||||
const { t } = useTranslation('meRoot')
|
||||
|
@ -26,7 +43,7 @@ const Login: React.FC = () => {
|
|||
clientSecret: string
|
||||
}>()
|
||||
|
||||
const { isSuccess, refetch, data } = useQuery(
|
||||
const { isSuccess, isFetching, refetch, data } = useQuery(
|
||||
['Instance', { instance }],
|
||||
instanceFetch,
|
||||
{
|
||||
|
@ -40,11 +57,13 @@ const Login: React.FC = () => {
|
|||
text => {
|
||||
setInstance(text)
|
||||
setApplicationData(undefined)
|
||||
if (text) {
|
||||
refetch()
|
||||
}
|
||||
},
|
||||
1000,
|
||||
{
|
||||
leading: true
|
||||
trailing: true
|
||||
}
|
||||
),
|
||||
[]
|
||||
|
@ -121,16 +140,70 @@ const Login: React.FC = () => {
|
|||
})()
|
||||
}, [response])
|
||||
|
||||
const infoRef = createRef<ShimmerPlaceholder>()
|
||||
|
||||
const instanceInfo = useCallback(
|
||||
({
|
||||
header,
|
||||
content,
|
||||
parse
|
||||
}: {
|
||||
header: string
|
||||
content: string
|
||||
parse?: boolean
|
||||
}) => {
|
||||
if (isFetching) {
|
||||
Animated.loop(infoRef.current?.getAnimated()!).start()
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.instanceInfo}>
|
||||
<Text style={[styles.instanceInfoHeader, { color: theme.primary }]}>
|
||||
{header}
|
||||
</Text>
|
||||
<ShimmerPlaceholder
|
||||
visible={data?.uri}
|
||||
stopAutoRun
|
||||
width={
|
||||
Dimensions.get('screen').width -
|
||||
StyleConstants.Spacing.Global.PagePadding * 4
|
||||
}
|
||||
height={StyleConstants.Font.Size.M}
|
||||
>
|
||||
<Text
|
||||
style={[styles.instanceInfoContent, { color: theme.primary }]}
|
||||
>
|
||||
{parse ? (
|
||||
<ParseContent content={content} size={'M'} numberOfLines={5} />
|
||||
) : (
|
||||
content
|
||||
)}
|
||||
</Text>
|
||||
</ShimmerPlaceholder>
|
||||
</View>
|
||||
)
|
||||
},
|
||||
[data?.uri, isFetching]
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style={{ flexDirection: 'row' }}>
|
||||
<Image
|
||||
source={require('assets/screens/meRoot/welcome.png')}
|
||||
style={{ resizeMode: 'contain', flex: 1, aspectRatio: 16 / 9 }}
|
||||
/>
|
||||
</View>
|
||||
<View style={styles.base}>
|
||||
<View style={styles.inputRow}>
|
||||
<TextInput
|
||||
style={{
|
||||
height: 50,
|
||||
style={[
|
||||
styles.textInput,
|
||||
{
|
||||
color: theme.primary,
|
||||
borderColor: theme.border,
|
||||
borderWidth: 1,
|
||||
padding: StyleConstants.Spacing.M
|
||||
}}
|
||||
borderBottomColor: theme.secondary
|
||||
}
|
||||
]}
|
||||
onChangeText={onChangeText}
|
||||
autoCapitalize='none'
|
||||
autoCorrect={false}
|
||||
|
@ -147,21 +220,137 @@ const Login: React.FC = () => {
|
|||
/>
|
||||
<ButtonRow
|
||||
onPress={async () => await createApplication()}
|
||||
text={t('content.login.button')}
|
||||
{...(isFetching
|
||||
? { icon: 'loader' }
|
||||
: { text: t('content.login.button') as string })}
|
||||
disabled={!data?.uri}
|
||||
/>
|
||||
{isSuccess && data && data.uri && (
|
||||
</View>
|
||||
<View>
|
||||
<Text style={{ color: theme.primary }}>{data.title}</Text>
|
||||
{instanceInfo({ header: '实例名称', content: data?.title })}
|
||||
{instanceInfo({
|
||||
header: '实例介绍',
|
||||
content: data?.short_description,
|
||||
parse: true
|
||||
})}
|
||||
<View style={styles.instanceStats}>
|
||||
<View style={styles.instanceStat}>
|
||||
<Text
|
||||
style={[styles.instanceInfoHeader, { color: theme.primary }]}
|
||||
>
|
||||
用户总数
|
||||
</Text>
|
||||
<ShimmerPlaceholder
|
||||
visible={data?.stats?.user_count}
|
||||
stopAutoRun
|
||||
width={StyleConstants.Font.Size.M * 4}
|
||||
height={StyleConstants.Font.Size.M}
|
||||
>
|
||||
<Text
|
||||
style={[styles.instanceInfoContent, { color: theme.primary }]}
|
||||
>
|
||||
{data?.stats?.user_count}
|
||||
</Text>
|
||||
</ShimmerPlaceholder>
|
||||
</View>
|
||||
)}
|
||||
<View style={[styles.instanceStat, { alignItems: 'center' }]}>
|
||||
<Text
|
||||
style={[styles.instanceInfoHeader, { color: theme.primary }]}
|
||||
>
|
||||
嘟嘟总数
|
||||
</Text>
|
||||
<ShimmerPlaceholder
|
||||
visible={data?.stats?.user_count}
|
||||
stopAutoRun
|
||||
width={StyleConstants.Font.Size.M * 4}
|
||||
height={StyleConstants.Font.Size.M}
|
||||
>
|
||||
<Text
|
||||
style={[styles.instanceInfoContent, { color: theme.primary }]}
|
||||
>
|
||||
{data?.stats?.status_count}
|
||||
</Text>
|
||||
</ShimmerPlaceholder>
|
||||
</View>
|
||||
<View style={[styles.instanceStat, { alignItems: 'flex-end' }]}>
|
||||
<Text
|
||||
style={[styles.instanceInfoHeader, { color: theme.primary }]}
|
||||
>
|
||||
连结总数
|
||||
</Text>
|
||||
<ShimmerPlaceholder
|
||||
visible={data?.stats?.user_count}
|
||||
stopAutoRun
|
||||
width={StyleConstants.Font.Size.M * 4}
|
||||
height={StyleConstants.Font.Size.M}
|
||||
>
|
||||
<Text
|
||||
style={[styles.instanceInfoContent, { color: theme.primary }]}
|
||||
>
|
||||
{data?.stats?.domain_count}
|
||||
</Text>
|
||||
</ShimmerPlaceholder>
|
||||
</View>
|
||||
</View>
|
||||
<Text style={[styles.disclaimer, { color: theme.secondary }]}>
|
||||
<Feather
|
||||
name='lock'
|
||||
size={StyleConstants.Font.Size.M}
|
||||
color={theme.secondary}
|
||||
/>{' '}
|
||||
本站不留存任何信息
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
base: {
|
||||
padding: StyleConstants.Spacing.Global.PagePadding
|
||||
},
|
||||
inputRow: {
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
},
|
||||
textInput: {
|
||||
flex: 1,
|
||||
borderBottomWidth: 1.25,
|
||||
paddingTop: StyleConstants.Spacing.S - 1.5,
|
||||
paddingBottom: StyleConstants.Spacing.S - 1.5,
|
||||
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingRight: StyleConstants.Spacing.Global.PagePadding,
|
||||
fontSize: StyleConstants.Font.Size.M,
|
||||
marginRight: StyleConstants.Spacing.M
|
||||
},
|
||||
instanceInfo: {
|
||||
marginTop: StyleConstants.Spacing.M,
|
||||
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingRight: StyleConstants.Spacing.Global.PagePadding
|
||||
},
|
||||
instanceInfoHeader: {
|
||||
fontSize: StyleConstants.Font.Size.S,
|
||||
fontWeight: StyleConstants.Font.Weight.Bold,
|
||||
marginBottom: StyleConstants.Spacing.XS
|
||||
},
|
||||
instanceInfoContent: { fontSize: StyleConstants.Font.Size.M },
|
||||
instanceStats: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
marginTop: StyleConstants.Spacing.M,
|
||||
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingRight: StyleConstants.Spacing.Global.PagePadding,
|
||||
marginBottom: StyleConstants.Spacing.M
|
||||
},
|
||||
instanceStat: {
|
||||
flex: 1
|
||||
},
|
||||
disclaimer: {
|
||||
fontSize: StyleConstants.Font.Size.S,
|
||||
paddingLeft: StyleConstants.Spacing.Global.PagePadding,
|
||||
paddingRight: StyleConstants.Spacing.Global.PagePadding,
|
||||
marginBottom: StyleConstants.Spacing.M
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -6,11 +6,13 @@ import MenuButton from '@components/Menu/Button'
|
|||
import { MenuContainer } from '@components/Menu'
|
||||
import { useNavigation } from '@react-navigation/native'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useQueryCache } from 'react-query'
|
||||
|
||||
const Logout: React.FC = () => {
|
||||
const { t } = useTranslation('meRoot')
|
||||
const dispatch = useDispatch()
|
||||
const navigation = useNavigation()
|
||||
const queryCache = useQueryCache()
|
||||
|
||||
const alertOption = {
|
||||
title: t('content.logout.alert.title'),
|
||||
|
@ -20,6 +22,7 @@ const Logout: React.FC = () => {
|
|||
text: t('content.logout.alert.buttons.logout'),
|
||||
style: 'destructive' as const,
|
||||
onPress: () => {
|
||||
queryCache.clear()
|
||||
dispatch(updateLocal({}))
|
||||
navigation.navigate('Screen-Public', {
|
||||
screen: 'Screen-Public-Root',
|
||||
|
|
|
@ -5,7 +5,6 @@ import Timeline from '@components/Timelines/Timeline'
|
|||
import sharedScreens from '@screens/Shared/sharedScreens'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { RootState } from '@root/store'
|
||||
import PleaseLogin from '@components/PleaseLogin'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
const Stack = createNativeStackNavigator()
|
||||
|
@ -21,9 +20,7 @@ const ScreenNotifications: React.FC = () => {
|
|||
screenOptions={{ headerTitle: t('notifications:heading') }}
|
||||
>
|
||||
<Stack.Screen name='Screen-Notifications-Root'>
|
||||
{() =>
|
||||
localRegistered ? <Timeline page='Notifications' /> : <PleaseLogin />
|
||||
}
|
||||
{() => (localRegistered ? <Timeline page='Notifications' /> : null)}
|
||||
</Stack.Screen>
|
||||
|
||||
{sharedScreens(Stack)}
|
||||
|
|
|
@ -25,7 +25,11 @@ import { emojisFetch } from '@utils/fetches/emojisFetch'
|
|||
import { searchFetch } from '@utils/fetches/searchFetch'
|
||||
import { StyleConstants } from '@utils/styles/constants'
|
||||
import { useTheme } from '@utils/styles/ThemeManager'
|
||||
import { PostAction, ComposeState, ComposeContext } from '@screens/Shared/Compose'
|
||||
import {
|
||||
PostAction,
|
||||
ComposeState,
|
||||
ComposeContext
|
||||
} from '@screens/Shared/Compose'
|
||||
import ComposeActions from '@screens/Shared/Compose/Actions'
|
||||
import updateText from './updateText'
|
||||
import * as Permissions from 'expo-permissions'
|
||||
|
@ -189,7 +193,7 @@ const ComposeRoot: React.FC = () => {
|
|||
/>
|
||||
<FlatList
|
||||
keyboardShouldPersistTaps='handled'
|
||||
ListHeaderComponent={<ComposeRootHeader textInputRef={textInputRef} />}
|
||||
ListHeaderComponent={<ComposeRootHeader />}
|
||||
ListFooterComponent={<ComposeRootFooter textInputRef={textInputRef} />}
|
||||
ListEmptyComponent={listEmpty}
|
||||
data={data}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"strict": true,
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
// "@assets/*": ["./assets/*"],
|
||||
"@root/*": ["./src/*"],
|
||||
"@api/*": ["./src/api/*"],
|
||||
"@components/*": ["./src/components/*"],
|
||||
|
|
Loading…
Reference in New Issue